summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/colour_conversion.cc1
-rw-r--r--src/lib/dcp_video_frame.cc114
-rw-r--r--src/lib/dcp_video_frame.h23
-rw-r--r--src/lib/encoder.cc35
-rw-r--r--src/lib/encoder.h10
-rw-r--r--src/lib/ffmpeg_decoder.cc5
-rw-r--r--src/lib/image.cc23
-rw-r--r--src/lib/image.h2
-rw-r--r--src/lib/image_decoder.cc30
-rw-r--r--src/lib/image_decoder.h2
-rw-r--r--src/lib/image_proxy.cc161
-rw-r--r--src/lib/image_proxy.h84
-rw-r--r--src/lib/piece.cc1
-rw-r--r--src/lib/piece.h5
-rw-r--r--src/lib/player.cc69
-rw-r--r--src/lib/player.h36
-rw-r--r--src/lib/player_video_frame.cc148
-rw-r--r--src/lib/player_video_frame.h66
-rw-r--r--src/lib/server.cc22
-rw-r--r--src/lib/transcoder.cc8
-rw-r--r--src/lib/types.cc22
-rw-r--r--src/lib/types.h22
-rw-r--r--src/lib/video_content.cc5
-rw-r--r--src/lib/video_decoder.cc24
-rw-r--r--src/lib/video_decoder.h9
-rw-r--r--src/lib/writer.h2
-rw-r--r--src/lib/wscript2
-rw-r--r--src/tools/server_test.cc9
-rw-r--r--src/wx/film_viewer.cc9
-rw-r--r--src/wx/film_viewer.h4
30 files changed, 695 insertions, 258 deletions
diff --git a/src/lib/colour_conversion.cc b/src/lib/colour_conversion.cc
index cd1a81b25..5f17f9184 100644
--- a/src/lib/colour_conversion.cc
+++ b/src/lib/colour_conversion.cc
@@ -29,6 +29,7 @@
using std::list;
using std::string;
+using std::stringstream;
using std::cout;
using std::vector;
using boost::shared_ptr;
diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc
index 1c12eb7fd..5cd6a118e 100644
--- a/src/lib/dcp_video_frame.cc
+++ b/src/lib/dcp_video_frame.cc
@@ -42,6 +42,7 @@
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
+#include <openssl/md5.h>
#include <libdcp/rec709_linearised_gamma_lut.h>
#include <libdcp/srgb_linearised_gamma_lut.h>
#include <libdcp/gamma_lut.h>
@@ -60,6 +61,7 @@
#include "image.h"
#include "log.h"
#include "cross.h"
+#include "player_video_frame.h"
#include "i18n.h"
@@ -73,18 +75,16 @@ using libdcp::raw_convert;
#define DCI_COEFFICENT (48.0 / 52.37)
/** Construct a DCP video frame.
- * @param input Input image.
- * @param f Index of the frame within the DCP.
+ * @param frame Input frame.
+ * @param index Index of the frame within the DCP.
* @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
* @param l Log to write to.
*/
DCPVideoFrame::DCPVideoFrame (
- shared_ptr<const Image> image, int f, Eyes eyes, ColourConversion c, int dcp_fps, int bw, Resolution r, shared_ptr<Log> l
+ shared_ptr<const PlayerVideoFrame> frame, int index, int dcp_fps, int bw, Resolution r, shared_ptr<Log> l
)
- : _image (image)
- , _frame (f)
- , _eyes (eyes)
- , _conversion (c)
+ : _frame (frame)
+ , _index (index)
, _frames_per_second (dcp_fps)
, _j2k_bandwidth (bw)
, _resolution (r)
@@ -93,22 +93,11 @@ DCPVideoFrame::DCPVideoFrame (
}
-DCPVideoFrame::DCPVideoFrame (shared_ptr<const Image> image, shared_ptr<const cxml::Node> node, shared_ptr<Log> log)
- : _image (image)
+DCPVideoFrame::DCPVideoFrame (shared_ptr<const PlayerVideoFrame> frame, shared_ptr<const cxml::Node> node, shared_ptr<Log> log)
+ : _frame (frame)
, _log (log)
{
- _frame = node->number_child<int> ("Frame");
- string const eyes = node->string_child ("Eyes");
- if (eyes == "Both") {
- _eyes = EYES_BOTH;
- } else if (eyes == "Left") {
- _eyes = EYES_LEFT;
- } else if (eyes == "Right") {
- _eyes = EYES_RIGHT;
- } else {
- assert (false);
- }
- _conversion = ColourConversion (node->node_child ("ColourConversion"));
+ _index = node->number_child<int> ("Index");
_frames_per_second = node->number_child<int> ("FramesPerSecond");
_j2k_bandwidth = node->number_child<int> ("J2KBandwidth");
_resolution = Resolution (node->optional_number_child<int>("Resolution").get_value_or (RESOLUTION_2K));
@@ -121,10 +110,10 @@ shared_ptr<EncodedData>
DCPVideoFrame::encode_locally ()
{
shared_ptr<libdcp::LUT> in_lut;
- if (_conversion.input_gamma_linearised) {
- in_lut = libdcp::SRGBLinearisedGammaLUT::cache.get (12, _conversion.input_gamma);
+ if (_frame->colour_conversion().input_gamma_linearised) {
+ in_lut = libdcp::SRGBLinearisedGammaLUT::cache.get (12, _frame->colour_conversion().input_gamma);
} else {
- in_lut = libdcp::GammaLUT::cache.get (12, _conversion.input_gamma);
+ in_lut = libdcp::GammaLUT::cache.get (12, _frame->colour_conversion().input_gamma);
}
/* XXX: libdcp should probably use boost */
@@ -132,20 +121,35 @@ DCPVideoFrame::encode_locally ()
double matrix[3][3];
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
- matrix[i][j] = _conversion.matrix (i, j);
+ matrix[i][j] = _frame->colour_conversion().matrix (i, j);
}
}
-
+
shared_ptr<libdcp::XYZFrame> xyz = libdcp::rgb_to_xyz (
- _image,
+ _frame->image(),
in_lut,
- libdcp::GammaLUT::cache.get (16, 1 / _conversion.output_gamma),
+ libdcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma),
matrix
);
+
+ {
+ MD5_CTX md5_context;
+ MD5_Init (&md5_context);
+ MD5_Update (&md5_context, xyz->data(0), 1998 * 1080 * 4);
+ MD5_Update (&md5_context, xyz->data(1), 1998 * 1080 * 4);
+ MD5_Update (&md5_context, xyz->data(2), 1998 * 1080 * 4);
+ unsigned char digest[MD5_DIGEST_LENGTH];
+ MD5_Final (digest, &md5_context);
+ stringstream s;
+ for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
+ s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
+ }
+ }
+
/* Set the max image and component sizes based on frame_rate */
int max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second;
- if (_eyes == EYES_LEFT || _eyes == EYES_RIGHT) {
+ if (_frame->eyes() == EYES_LEFT || _frame->eyes() == EYES_RIGHT) {
/* In 3D we have only half the normal bandwidth per eye */
max_cs_len /= 2;
}
@@ -244,15 +248,15 @@ DCPVideoFrame::encode_locally ()
throw EncodeError (N_("JPEG2000 encoding failed"));
}
- switch (_eyes) {
+ switch (_frame->eyes()) {
case EYES_BOTH:
- _log->log (String::compose (N_("Finished locally-encoded frame %1 for mono"), _frame));
+ _log->log (String::compose (N_("Finished locally-encoded frame %1 for mono"), _index));
break;
case EYES_LEFT:
- _log->log (String::compose (N_("Finished locally-encoded frame %1 for L"), _frame));
+ _log->log (String::compose (N_("Finished locally-encoded frame %1 for L"), _index));
break;
case EYES_RIGHT:
- _log->log (String::compose (N_("Finished locally-encoded frame %1 for R"), _frame));
+ _log->log (String::compose (N_("Finished locally-encoded frame %1 for R"), _index));
break;
default:
break;
@@ -283,28 +287,30 @@ DCPVideoFrame::encode_remotely (ServerDescription serv)
socket->connect (*endpoint_iterator);
+ /* Collect all XML metadata */
xmlpp::Document doc;
xmlpp::Element* root = doc.create_root_node ("EncodingRequest");
-
root->add_child("Version")->add_child_text (raw_convert<string> (SERVER_LINK_VERSION));
- root->add_child("Width")->add_child_text (raw_convert<string> (_image->size().width));
- root->add_child("Height")->add_child_text (raw_convert<string> (_image->size().height));
add_metadata (root);
+ _log->log (String::compose (N_("Sending frame %1 to remote"), _index));
+
+ /* Send XML metadata */
stringstream xml;
doc.write_to_stream (xml, "UTF-8");
-
- _log->log (String::compose (N_("Sending frame %1 to remote"), _frame));
-
socket->write (xml.str().length() + 1);
socket->write ((uint8_t *) xml.str().c_str(), xml.str().length() + 1);
- _image->write_to_socket (socket);
+ /* Send binary data */
+ _frame->send_binary (socket);
+ /* Read the response (JPEG2000-encoded data); this blocks until the data
+ is ready and sent back.
+ */
shared_ptr<EncodedData> e (new RemotelyEncodedData (socket->read_uint32 ()));
socket->read (e->data(), e->size());
- _log->log (String::compose (N_("Finished remotely-encoded frame %1"), _frame));
+ _log->log (String::compose (N_("Finished remotely-encoded frame %1"), _index));
return e;
}
@@ -312,27 +318,17 @@ DCPVideoFrame::encode_remotely (ServerDescription serv)
void
DCPVideoFrame::add_metadata (xmlpp::Element* el) const
{
- el->add_child("Frame")->add_child_text (raw_convert<string> (_frame));
-
- switch (_eyes) {
- case EYES_BOTH:
- el->add_child("Eyes")->add_child_text ("Both");
- break;
- case EYES_LEFT:
- el->add_child("Eyes")->add_child_text ("Left");
- break;
- case EYES_RIGHT:
- el->add_child("Eyes")->add_child_text ("Right");
- break;
- default:
- assert (false);
- }
-
- _conversion.as_xml (el->add_child("ColourConversion"));
-
+ el->add_child("Index")->add_child_text (raw_convert<string> (_index));
el->add_child("FramesPerSecond")->add_child_text (raw_convert<string> (_frames_per_second));
el->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
el->add_child("Resolution")->add_child_text (raw_convert<string> (int (_resolution)));
+ _frame->add_metadata (el);
+}
+
+Eyes
+DCPVideoFrame::eyes () const
+{
+ return _frame->eyes ();
}
EncodedData::EncodedData (int s)
diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h
index 40f758c74..e4006d986 100644
--- a/src/lib/dcp_video_frame.h
+++ b/src/lib/dcp_video_frame.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
This program is free software; you can redistribute it and/or modify
@@ -33,6 +33,7 @@ class Scaler;
class Image;
class Log;
class Subtitle;
+class PlayerVideoFrame;
/** @class EncodedData
* @brief Container for J2K-encoded data.
@@ -102,28 +103,24 @@ public:
class DCPVideoFrame : public boost::noncopyable
{
public:
- DCPVideoFrame (boost::shared_ptr<const Image>, int, Eyes, ColourConversion, int, int, Resolution, boost::shared_ptr<Log>);
- DCPVideoFrame (boost::shared_ptr<const Image>, boost::shared_ptr<const cxml::Node>, boost::shared_ptr<Log>);
+ DCPVideoFrame (boost::shared_ptr<const PlayerVideoFrame>, int, int, int, Resolution, boost::shared_ptr<Log>);
+ DCPVideoFrame (boost::shared_ptr<const PlayerVideoFrame>, boost::shared_ptr<const cxml::Node>, boost::shared_ptr<Log>);
boost::shared_ptr<EncodedData> encode_locally ();
boost::shared_ptr<EncodedData> encode_remotely (ServerDescription);
- Eyes eyes () const {
- return _eyes;
- }
-
- int frame () const {
- return _frame;
+ int index () const {
+ return _index;
}
+
+ Eyes eyes () const;
private:
void add_metadata (xmlpp::Element *) const;
- boost::shared_ptr<const Image> _image;
- int _frame; ///< frame index within the DCP's intrinsic duration
- Eyes _eyes;
- ColourConversion _conversion;
+ boost::shared_ptr<const PlayerVideoFrame> _frame;
+ int _index; ///< frame index within the DCP's intrinsic duration
int _frames_per_second; ///< Frames per second that we will use for the DCP
int _j2k_bandwidth; ///< J2K bandwidth to use
Resolution _resolution; ///< Resolution (2K or 4K)
diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc
index 8e8da6229..4fc2d7f81 100644
--- a/src/lib/encoder.cc
+++ b/src/lib/encoder.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@
#include "writer.h"
#include "server_finder.h"
#include "player.h"
+#include "player_video_frame.h"
#include "i18n.h"
@@ -124,9 +125,9 @@ Encoder::process_end ()
*/
for (list<shared_ptr<DCPVideoFrame> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
- _film->log()->log (String::compose (N_("Encode left-over frame %1"), (*i)->frame ()));
+ _film->log()->log (String::compose (N_("Encode left-over frame %1"), (*i)->index ()));
try {
- _writer->write ((*i)->encode_locally(), (*i)->frame (), (*i)->eyes ());
+ _writer->write ((*i)->encode_locally(), (*i)->index (), (*i)->eyes ());
frame_done ();
} catch (std::exception& e) {
_film->log()->log (String::compose (N_("Local encode failed (%1)"), e.what ()));
@@ -179,7 +180,7 @@ Encoder::frame_done ()
}
void
-Encoder::process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, bool same)
+Encoder::process_video (shared_ptr<PlayerVideoFrame> pvf, bool same)
{
_waker.nudge ();
@@ -206,28 +207,28 @@ Encoder::process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversi
rethrow ();
if (_writer->can_fake_write (_video_frames_out)) {
- _writer->fake_write (_video_frames_out, eyes);
- _have_a_real_frame[eyes] = false;
+ _writer->fake_write (_video_frames_out, pvf->eyes ());
+ _have_a_real_frame[pvf->eyes()] = false;
frame_done ();
- } else if (same && _have_a_real_frame[eyes]) {
+ } else if (same && _have_a_real_frame[pvf->eyes()]) {
/* Use the last frame that we encoded. */
- _writer->repeat (_video_frames_out, eyes);
+ _writer->repeat (_video_frames_out, pvf->eyes());
frame_done ();
} else {
/* Queue this new frame for encoding */
TIMING ("adding to queue of %1", _queue.size ());
_queue.push_back (shared_ptr<DCPVideoFrame> (
new DCPVideoFrame (
- image->image(), _video_frames_out, eyes, conversion, _film->video_frame_rate(),
+ pvf, _video_frames_out, _film->video_frame_rate(),
_film->j2k_bandwidth(), _film->resolution(), _film->log()
)
));
_condition.notify_all ();
- _have_a_real_frame[eyes] = true;
+ _have_a_real_frame[pvf->eyes()] = true;
}
- if (eyes != EYES_LEFT) {
+ if (pvf->eyes() != EYES_LEFT) {
++_video_frames_out;
}
}
@@ -281,7 +282,7 @@ try
TIMING ("encoder thread %1 wakes with queue of %2", boost::this_thread::get_id(), _queue.size());
shared_ptr<DCPVideoFrame> vf = _queue.front ();
- TIMING ("encoder thread %1 pops frame %2 (%3) from queue", boost::this_thread::get_id(), vf->frame(), vf->eyes ());
+ TIMING ("encoder thread %1 pops frame %2 (%3) from queue", boost::this_thread::get_id(), vf->index(), vf->eyes ());
_queue.pop_front ();
lock.unlock ();
@@ -307,27 +308,27 @@ try
_film->log()->log (
String::compose (
N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
- vf->frame(), server->host_name(), e.what(), remote_backoff)
+ vf->index(), server->host_name(), e.what(), remote_backoff)
);
}
} else {
try {
- TIMING ("encoder thread %1 begins local encode of %2", boost::this_thread::get_id(), vf->frame());
+ TIMING ("encoder thread %1 begins local encode of %2", boost::this_thread::get_id(), vf->index());
encoded = vf->encode_locally ();
- TIMING ("encoder thread %1 finishes local encode of %2", boost::this_thread::get_id(), vf->frame());
+ TIMING ("encoder thread %1 finishes local encode of %2", boost::this_thread::get_id(), vf->index());
} catch (std::exception& e) {
_film->log()->log (String::compose (N_("Local encode failed (%1)"), e.what ()));
}
}
if (encoded) {
- _writer->write (encoded, vf->frame (), vf->eyes ());
+ _writer->write (encoded, vf->index (), vf->eyes ());
frame_done ();
} else {
lock.lock ();
_film->log()->log (
- String::compose (N_("Encoder thread %1 pushes frame %2 back onto queue after failure"), boost::this_thread::get_id(), vf->frame())
+ String::compose (N_("Encoder thread %1 pushes frame %2 back onto queue after failure"), boost::this_thread::get_id(), vf->index())
);
_queue.push_front (vf);
lock.unlock ();
diff --git a/src/lib/encoder.h b/src/lib/encoder.h
index e0ee2d414..a8ee220aa 100644
--- a/src/lib/encoder.h
+++ b/src/lib/encoder.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -48,7 +48,7 @@ class EncodedData;
class Writer;
class Job;
class ServerFinder;
-class PlayerImage;
+class PlayerVideoFrame;
/** @class Encoder
* @brief Encoder to J2K and WAV for DCP.
@@ -67,10 +67,10 @@ public:
void process_begin ();
/** Call with a frame of video.
- * @param i Video frame image.
- * @param same true if i is the same as the last time we were called.
+ * @param pvf Video frame image.
+ * @param same true if pvf is the same as the last time we were called.
*/
- void process_video (boost::shared_ptr<PlayerImage> i, Eyes eyes, ColourConversion, bool same);
+ void process_video (boost::shared_ptr<PlayerVideoFrame> pvf, bool same);
/** Call with some audio data */
void process_audio (boost::shared_ptr<const AudioBuffers>);
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index c93012608..7a5bf8ba8 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -42,6 +42,7 @@ extern "C" {
#include "filter_graph.h"
#include "audio_buffers.h"
#include "ffmpeg_content.h"
+#include "image_proxy.h"
#include "i18n.h"
@@ -502,13 +503,13 @@ FFmpegDecoder::decode_video_packet ()
);
black->make_black ();
- video (image, false, _video_position);
+ video (shared_ptr<ImageProxy> (new RawImageProxy (image)), false, _video_position);
delta -= one_frame;
}
if (delta > -one_frame) {
/* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */
- video (image, false, _video_position);
+ video (shared_ptr<ImageProxy> (new RawImageProxy (image)), false, _video_position);
}
} else {
diff --git a/src/lib/image.cc b/src/lib/image.cc
index d083cf3f6..1fa55e242 100644
--- a/src/lib/image.cc
+++ b/src/lib/image.cc
@@ -22,6 +22,7 @@
*/
#include <iostream>
+#include <openssl/md5.h>
extern "C" {
#include <libswscale/swscale.h>
#include <libavutil/pixfmt.h>
@@ -37,6 +38,7 @@ using std::string;
using std::min;
using std::cout;
using std::cerr;
+using std::stringstream;
using boost::shared_ptr;
using libdcp::Size;
@@ -619,3 +621,24 @@ Image::aligned () const
return _aligned;
}
+string
+Image::digest () const
+{
+ MD5_CTX md5_context;
+ MD5_Init (&md5_context);
+
+ for (int i = 0; i < components(); ++i) {
+ MD5_Update (&md5_context, data()[i], line_size()[i]);
+ }
+
+ unsigned char digest[MD5_DIGEST_LENGTH];
+ MD5_Final (digest, &md5_context);
+
+ stringstream s;
+ for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
+ s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
+ }
+
+ return s.str ();
+}
+
diff --git a/src/lib/image.h b/src/lib/image.h
index 2d9f32231..f83bf6998 100644
--- a/src/lib/image.h
+++ b/src/lib/image.h
@@ -73,6 +73,8 @@ public:
return _pixel_format;
}
+ std::string digest () const;
+
private:
friend class pixel_formats_test;
diff --git a/src/lib/image_decoder.cc b/src/lib/image_decoder.cc
index a7999c02a..d33b64cd4 100644
--- a/src/lib/image_decoder.cc
+++ b/src/lib/image_decoder.cc
@@ -23,6 +23,7 @@
#include "image_content.h"
#include "image_decoder.h"
#include "image.h"
+#include "image_proxy.h"
#include "film.h"
#include "exceptions.h"
@@ -52,34 +53,7 @@ ImageDecoder::pass ()
return;
}
- Magick::Image* magick_image = 0;
- boost::filesystem::path const path = _image_content->path (_image_content->still() ? 0 : _video_position);
- try {
- magick_image = new Magick::Image (path.string ());
- } catch (...) {
- throw OpenFileError (path);
- }
-
- libdcp::Size size (magick_image->columns(), magick_image->rows());
-
- _image.reset (new Image (PIX_FMT_RGB24, size, true));
-
- using namespace MagickCore;
-
- uint8_t* p = _image->data()[0];
- for (int y = 0; y < size.height; ++y) {
- uint8_t* q = p;
- for (int x = 0; x < size.width; ++x) {
- Magick::Color c = magick_image->pixelColor (x, y);
- *q++ = c.redQuantum() * 255 / QuantumRange;
- *q++ = c.greenQuantum() * 255 / QuantumRange;
- *q++ = c.blueQuantum() * 255 / QuantumRange;
- }
- p += _image->stride()[0];
- }
-
- delete magick_image;
-
+ _image.reset (new MagickImageProxy (_image_content->path (_image_content->still() ? 0 : _video_position)));
video (_image, false, _video_position);
}
diff --git a/src/lib/image_decoder.h b/src/lib/image_decoder.h
index c7500243e..5b82dd85c 100644
--- a/src/lib/image_decoder.h
+++ b/src/lib/image_decoder.h
@@ -42,6 +42,6 @@ public:
private:
boost::shared_ptr<const ImageContent> _image_content;
- boost::shared_ptr<Image> _image;
+ boost::shared_ptr<ImageProxy> _image;
};
diff --git a/src/lib/image_proxy.cc b/src/lib/image_proxy.cc
new file mode 100644
index 000000000..47ac5d372
--- /dev/null
+++ b/src/lib/image_proxy.cc
@@ -0,0 +1,161 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <Magick++.h>
+#include <libdcp/util.h>
+#include <libdcp/raw_convert.h>
+#include "image_proxy.h"
+#include "image.h"
+#include "exceptions.h"
+#include "cross.h"
+
+#include "i18n.h"
+
+using std::cout;
+using std::string;
+using std::stringstream;
+using boost::shared_ptr;
+
+RawImageProxy::RawImageProxy (shared_ptr<Image> image)
+ : _image (image)
+{
+
+}
+
+RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
+{
+ libdcp::Size size (
+ xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
+ );
+
+ _image.reset (new Image (PIX_FMT_RGB24, size, true));
+ _image->read_from_socket (socket);
+}
+
+shared_ptr<Image>
+RawImageProxy::image () const
+{
+ return _image;
+}
+
+void
+RawImageProxy::add_metadata (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text (N_("Raw"));
+ node->add_child("Width")->add_child_text (libdcp::raw_convert<string> (_image->size().width));
+ node->add_child("Height")->add_child_text (libdcp::raw_convert<string> (_image->size().height));
+}
+
+void
+RawImageProxy::send_binary (shared_ptr<Socket> socket) const
+{
+ _image->write_to_socket (socket);
+}
+
+MagickImageProxy::MagickImageProxy (boost::filesystem::path path)
+{
+ /* Read the file into a Blob */
+
+ boost::uintmax_t const size = boost::filesystem::file_size (path);
+ FILE* f = fopen_boost (path, "rb");
+ if (!f) {
+ throw OpenFileError (path);
+ }
+
+ uint8_t* data = new uint8_t[size];
+ if (fread (data, 1, size, f) != size) {
+ delete[] data;
+ throw ReadFileError (path);
+ }
+
+ fclose (f);
+ _blob.update (data, size);
+ delete[] data;
+}
+
+MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket)
+{
+ uint32_t const size = socket->read_uint32 ();
+ uint8_t* data = new uint8_t[size];
+ socket->read (data, size);
+ _blob.update (data, size);
+ delete[] data;
+}
+
+shared_ptr<Image>
+MagickImageProxy::image () const
+{
+ if (_image) {
+ return _image;
+ }
+
+ Magick::Image* magick_image = 0;
+ try {
+ magick_image = new Magick::Image (_blob);
+ } catch (...) {
+ throw DecodeError (_("Could not decode image file"));
+ }
+
+ libdcp::Size size (magick_image->columns(), magick_image->rows());
+
+ _image.reset (new Image (PIX_FMT_RGB24, size, true));
+
+ using namespace MagickCore;
+
+ uint8_t* p = _image->data()[0];
+ for (int y = 0; y < size.height; ++y) {
+ uint8_t* q = p;
+ for (int x = 0; x < size.width; ++x) {
+ Magick::Color c = magick_image->pixelColor (x, y);
+ *q++ = c.redQuantum() * 255 / QuantumRange;
+ *q++ = c.greenQuantum() * 255 / QuantumRange;
+ *q++ = c.blueQuantum() * 255 / QuantumRange;
+ }
+ p += _image->stride()[0];
+ }
+
+ delete magick_image;
+
+ return _image;
+}
+
+void
+MagickImageProxy::add_metadata (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text (N_("Magick"));
+}
+
+void
+MagickImageProxy::send_binary (shared_ptr<Socket> socket) const
+{
+ socket->write (_blob.length ());
+ socket->write ((uint8_t *) _blob.data (), _blob.length ());
+}
+
+shared_ptr<ImageProxy>
+image_proxy_factory (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
+{
+ if (xml->string_child("Type") == N_("Raw")) {
+ return shared_ptr<ImageProxy> (new RawImageProxy (xml, socket));
+ } else if (xml->string_child("Type") == N_("Magick")) {
+ return shared_ptr<MagickImageProxy> (new MagickImageProxy (xml, socket));
+ }
+
+ throw NetworkError (_("Unexpected image type received by server"));
+}
diff --git a/src/lib/image_proxy.h b/src/lib/image_proxy.h
new file mode 100644
index 000000000..792fa004a
--- /dev/null
+++ b/src/lib/image_proxy.h
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file src/lib/image_proxy.h
+ * @brief ImageProxy and subclasses.
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <boost/filesystem.hpp>
+#include <Magick++.h>
+#include <libxml++/libxml++.h>
+
+class Image;
+class Socket;
+
+namespace cxml {
+ class Node;
+}
+
+/** @class ImageProxy
+ * @brief A class which holds an Image, and can produce it on request.
+ *
+ * This is so that decoding of source images can be postponed until
+ * the encoder thread, where multi-threading is happening, instead
+ * 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.
+ * At this point, the class decodes the TIFF to an Image.
+ */
+class ImageProxy
+{
+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;
+};
+
+class RawImageProxy : public ImageProxy
+{
+public:
+ RawImageProxy (boost::shared_ptr<Image>);
+ RawImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
+
+ boost::shared_ptr<Image> image () const;
+ void add_metadata (xmlpp::Node *) const;
+ void send_binary (boost::shared_ptr<Socket>) const;
+
+private:
+ boost::shared_ptr<Image> _image;
+};
+
+class MagickImageProxy : public ImageProxy
+{
+public:
+ MagickImageProxy (boost::filesystem::path);
+ MagickImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
+
+ boost::shared_ptr<Image> image () const;
+ void add_metadata (xmlpp::Node *) const;
+ void send_binary (boost::shared_ptr<Socket>) const;
+
+private:
+ Magick::Blob _blob;
+ mutable boost::shared_ptr<Image> _image;
+};
+
+boost::shared_ptr<ImageProxy> image_proxy_factory (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
diff --git a/src/lib/piece.cc b/src/lib/piece.cc
index 3c39ecfb8..494fb17a0 100644
--- a/src/lib/piece.cc
+++ b/src/lib/piece.cc
@@ -73,6 +73,7 @@ Piece::repeat (Player* player)
repeat_video.weak_piece,
repeat_video.image,
repeat_video.eyes,
+ repeat_video.part,
repeat_done > 0,
repeat_video.frame,
(repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ())
diff --git a/src/lib/piece.h b/src/lib/piece.h
index 76df909ff..17b87b884 100644
--- a/src/lib/piece.h
+++ b/src/lib/piece.h
@@ -26,15 +26,16 @@
class Content;
class Decoder;
class Piece;
-class Image;
+class ImageProxy;
class Player;
struct IncomingVideo
{
public:
boost::weak_ptr<Piece> weak_piece;
- boost::shared_ptr<const Image> image;
+ boost::shared_ptr<const ImageProxy> image;
Eyes eyes;
+ Part part;
bool same;
VideoContent::Frame frame;
Time extra;
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 1bb2e7cf4..9f0f380e3 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -30,10 +30,12 @@
#include "playlist.h"
#include "job.h"
#include "image.h"
+#include "image_proxy.h"
#include "ratio.h"
#include "resampler.h"
#include "log.h"
#include "scaler.h"
+#include "player_video_frame.h"
using std::list;
using std::cout;
@@ -178,12 +180,13 @@ Player::pass ()
/** @param extra Amount of extra time to add to the content frame's time (for repeat) */
void
-Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, Eyes eyes, bool same, VideoContent::Frame frame, Time extra)
+Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const ImageProxy> image, Eyes eyes, Part part, bool same, VideoContent::Frame frame, Time extra)
{
/* Keep a note of what came in so that we can repeat it if required */
_last_incoming_video.weak_piece = weak_piece;
_last_incoming_video.image = image;
_last_incoming_video.eyes = eyes;
+ _last_incoming_video.part = part;
_last_incoming_video.same = same;
_last_incoming_video.frame = frame;
_last_incoming_video.extra = extra;
@@ -209,13 +212,16 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
Time const time = content->position() + relative_time + extra - content->trim_start ();
libdcp::Size const image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
- shared_ptr<PlayerImage> pi (
- new PlayerImage (
+ shared_ptr<PlayerVideoFrame> pi (
+ new PlayerVideoFrame (
image,
content->crop(),
image_size,
_video_container_size,
- _film->scaler()
+ _film->scaler(),
+ eyes,
+ part,
+ content->colour_conversion()
)
);
@@ -251,7 +257,7 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
_last_video = piece->content;
#endif
- Video (pi, eyes, content->colour_conversion(), same, time);
+ Video (pi, same, time);
_last_emit_was_black = false;
_video_position = piece->video_position = (time + TIME_HZ / _film->video_frame_rate());
@@ -418,7 +424,7 @@ Player::setup_pieces ()
if (fc) {
shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
- fd->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
+ fd->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, _5, 0));
fd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
fd->Subtitle.connect (bind (&Player::process_subtitle, this, weak_ptr<Piece> (piece), _1, _2, _3, _4));
@@ -441,7 +447,7 @@ Player::setup_pieces ()
if (!reusing) {
shared_ptr<ImageDecoder> id (new ImageDecoder (_film, ic));
- id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
+ id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, _5, 0));
piece->decoder = id;
}
}
@@ -519,12 +525,15 @@ Player::set_video_container_size (libdcp::Size s)
im->make_black ();
_black_frame.reset (
- new PlayerImage (
- im,
+ new PlayerVideoFrame (
+ shared_ptr<ImageProxy> (new RawImageProxy (im)),
Crop(),
_video_container_size,
_video_container_size,
- Scaler::from_id ("bicubic")
+ Scaler::from_id ("bicubic"),
+ EYES_BOTH,
+ PART_WHOLE,
+ ColourConversion ()
)
);
}
@@ -559,7 +568,7 @@ Player::emit_black ()
_last_video.reset ();
#endif
- Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
+ Video (_black_frame, _last_emit_was_black, _video_position);
_video_position += _film->video_frames_to_time (1);
_last_emit_was_black = true;
}
@@ -618,6 +627,7 @@ Player::repeat_last_video ()
_last_incoming_video.weak_piece,
_last_incoming_video.image,
_last_incoming_video.eyes,
+ _last_incoming_video.part,
_last_incoming_video.same,
_last_incoming_video.frame,
_last_incoming_video.extra
@@ -625,40 +635,3 @@ Player::repeat_last_video ()
return true;
}
-
-PlayerImage::PlayerImage (
- shared_ptr<const Image> in,
- Crop crop,
- libdcp::Size inter_size,
- libdcp::Size out_size,
- Scaler const * scaler
- )
- : _in (in)
- , _crop (crop)
- , _inter_size (inter_size)
- , _out_size (out_size)
- , _scaler (scaler)
-{
-
-}
-
-void
-PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
-{
- _subtitle_image = image;
- _subtitle_position = pos;
-}
-
-shared_ptr<Image>
-PlayerImage::image ()
-{
- shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
-
- Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
-
- if (_subtitle_image) {
- out->alpha_blend (_subtitle_image, _subtitle_position);
- }
-
- return out;
-}
diff --git a/src/lib/player.h b/src/lib/player.h
index 4d911a83b..bf6260c0a 100644
--- a/src/lib/player.h
+++ b/src/lib/player.h
@@ -39,28 +39,8 @@ class AudioContent;
class Piece;
class Image;
class Resampler;
-
-/** A wrapper for an Image which contains some pending operations; these may
- * not be necessary if the receiver of the PlayerImage throws it away.
- */
-class PlayerImage
-{
-public:
- PlayerImage (boost::shared_ptr<const Image>, Crop, libdcp::Size, libdcp::Size, Scaler const *);
-
- void set_subtitle (boost::shared_ptr<const Image>, Position<int>);
-
- boost::shared_ptr<Image> image ();
-
-private:
- boost::shared_ptr<const Image> _in;
- Crop _crop;
- libdcp::Size _inter_size;
- libdcp::Size _out_size;
- Scaler const * _scaler;
- boost::shared_ptr<const Image> _subtitle_image;
- Position<int> _subtitle_position;
-};
+class PlayerVideoFrame;
+class ImageProxy;
/** @class Player
* @brief A class which can `play' a Playlist; emitting its audio and video.
@@ -86,12 +66,10 @@ public:
/** Emitted when a video frame is ready.
* First parameter is the video image.
- * Second parameter is the eye(s) that should see this image.
- * Third parameter is the colour conversion that should be used for this image.
- * Fourth parameter is true if the image is the same as the last one that was emitted.
- * Fifth parameter is the time.
+ * Second parameter is true if the frame is the same as the last one that was emitted.
+ * Third parameter is the time.
*/
- boost::signals2::signal<void (boost::shared_ptr<PlayerImage>, Eyes, ColourConversion, bool, Time)> Video;
+ boost::signals2::signal<void (boost::shared_ptr<PlayerVideoFrame>, bool, Time)> Video;
/** Emitted when some audio data is ready */
boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio;
@@ -108,7 +86,7 @@ private:
friend class PlayerWrapper;
friend class Piece;
- void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame, Time);
+ void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const ImageProxy>, Eyes, Part, bool, VideoContent::Frame, Time);
void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
void process_subtitle (boost::weak_ptr<Piece>, boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time);
void setup_pieces ();
@@ -140,7 +118,7 @@ private:
AudioMerger<Time, AudioContent::Frame> _audio_merger;
libdcp::Size _video_container_size;
- boost::shared_ptr<PlayerImage> _black_frame;
+ boost::shared_ptr<PlayerVideoFrame> _black_frame;
std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
std::list<Subtitle> _subtitles;
diff --git a/src/lib/player_video_frame.cc b/src/lib/player_video_frame.cc
new file mode 100644
index 000000000..c96ed3a33
--- /dev/null
+++ b/src/lib/player_video_frame.cc
@@ -0,0 +1,148 @@
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libdcp/raw_convert.h>
+#include "player_video_frame.h"
+#include "image.h"
+#include "image_proxy.h"
+#include "scaler.h"
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+using libdcp::raw_convert;
+
+PlayerVideoFrame::PlayerVideoFrame (
+ shared_ptr<const ImageProxy> in,
+ Crop crop,
+ libdcp::Size inter_size,
+ libdcp::Size out_size,
+ Scaler const * scaler,
+ Eyes eyes,
+ Part part,
+ ColourConversion colour_conversion
+ )
+ : _in (in)
+ , _crop (crop)
+ , _inter_size (inter_size)
+ , _out_size (out_size)
+ , _scaler (scaler)
+ , _eyes (eyes)
+ , _part (part)
+ , _colour_conversion (colour_conversion)
+{
+
+}
+
+PlayerVideoFrame::PlayerVideoFrame (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket)
+{
+ _crop = Crop (node);
+
+ _inter_size = libdcp::Size (node->number_child<int> ("InterWidth"), node->number_child<int> ("InterHeight"));
+ _out_size = libdcp::Size (node->number_child<int> ("OutWidth"), node->number_child<int> ("OutHeight"));
+ _scaler = Scaler::from_id (node->string_child ("Scaler"));
+ _eyes = (Eyes) node->number_child<int> ("Eyes");
+ _part = (Part) node->number_child<int> ("Part");
+ _colour_conversion = ColourConversion (node);
+
+ _in = image_proxy_factory (node->node_child ("In"), socket);
+
+ if (node->optional_number_child<int> ("SubtitleX")) {
+
+ _subtitle_position = Position<int> (node->number_child<int> ("SubtitleX"), node->number_child<int> ("SubtitleY"));
+
+ shared_ptr<Image> image (
+ new Image (PIX_FMT_RGBA, libdcp::Size (node->number_child<int> ("SubtitleWidth"), node->number_child<int> ("SubtitleHeight")), true)
+ );
+
+ image->read_from_socket (socket);
+ _subtitle_image = image;
+ }
+}
+
+void
+PlayerVideoFrame::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
+{
+ _subtitle_image = image;
+ _subtitle_position = pos;
+}
+
+shared_ptr<Image>
+PlayerVideoFrame::image () const
+{
+ shared_ptr<Image> im = _in->image ();
+
+ Crop total_crop = _crop;
+ switch (_part) {
+ case PART_LEFT_HALF:
+ total_crop.right += im->size().width / 2;
+ break;
+ case PART_RIGHT_HALF:
+ total_crop.left += im->size().width / 2;
+ break;
+ case PART_TOP_HALF:
+ total_crop.bottom += im->size().height / 2;
+ break;
+ case PART_BOTTOM_HALF:
+ total_crop.top += im->size().height / 2;
+ break;
+ default:
+ break;
+ }
+
+ shared_ptr<Image> out = im->crop_scale_window (total_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
+
+ Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
+
+ if (_subtitle_image) {
+ out->alpha_blend (_subtitle_image, _subtitle_position);
+ }
+
+ return out;
+}
+
+void
+PlayerVideoFrame::add_metadata (xmlpp::Node* node) const
+{
+ _crop.as_xml (node);
+ _in->add_metadata (node->add_child ("In"));
+ node->add_child("InterWidth")->add_child_text (raw_convert<string> (_inter_size.width));
+ node->add_child("InterHeight")->add_child_text (raw_convert<string> (_inter_size.height));
+ node->add_child("OutWidth")->add_child_text (raw_convert<string> (_out_size.width));
+ node->add_child("OutHeight")->add_child_text (raw_convert<string> (_out_size.height));
+ node->add_child("Scaler")->add_child_text (_scaler->id ());
+ node->add_child("Eyes")->add_child_text (raw_convert<string> (_eyes));
+ node->add_child("Part")->add_child_text (raw_convert<string> (_part));
+ _colour_conversion.as_xml (node);
+ if (_subtitle_image) {
+ node->add_child ("SubtitleWidth")->add_child_text (raw_convert<string> (_subtitle_image->size().width));
+ node->add_child ("SubtitleHeight")->add_child_text (raw_convert<string> (_subtitle_image->size().height));
+ node->add_child ("SubtitleX")->add_child_text (raw_convert<string> (_subtitle_position.x));
+ node->add_child ("SubtitleY")->add_child_text (raw_convert<string> (_subtitle_position.y));
+ }
+}
+
+void
+PlayerVideoFrame::send_binary (shared_ptr<Socket> socket) const
+{
+ _in->send_binary (socket);
+ if (_subtitle_image) {
+ _subtitle_image->write_to_socket (socket);
+ }
+}
diff --git a/src/lib/player_video_frame.h b/src/lib/player_video_frame.h
new file mode 100644
index 000000000..6461134a9
--- /dev/null
+++ b/src/lib/player_video_frame.h
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/shared_ptr.hpp>
+#include "types.h"
+#include "position.h"
+#include "colour_conversion.h"
+
+class Image;
+class ImageProxy;
+class Scaler;
+class Socket;
+
+/** Everything needed to describe a video frame coming out of the player, but with the
+ * bits still their raw form. We may want to combine the bits on a remote machine,
+ * or maybe not even bother to combine them at all.
+ */
+class PlayerVideoFrame
+{
+public:
+ PlayerVideoFrame (boost::shared_ptr<const ImageProxy>, Crop, libdcp::Size, libdcp::Size, Scaler const *, Eyes, Part, ColourConversion);
+ PlayerVideoFrame (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>);
+
+ void set_subtitle (boost::shared_ptr<const Image>, Position<int>);
+
+ boost::shared_ptr<Image> image () const;
+
+ void add_metadata (xmlpp::Node* node) const;
+ void send_binary (boost::shared_ptr<Socket> socket) const;
+
+ Eyes eyes () const {
+ return _eyes;
+ }
+
+ ColourConversion colour_conversion () const {
+ return _colour_conversion;
+ }
+
+private:
+ boost::shared_ptr<const ImageProxy> _in;
+ Crop _crop;
+ libdcp::Size _inter_size;
+ libdcp::Size _out_size;
+ Scaler const * _scaler;
+ Eyes _eyes;
+ Part _part;
+ ColourConversion _colour_conversion;
+ boost::shared_ptr<const Image> _subtitle_image;
+ Position<int> _subtitle_position;
+};
diff --git a/src/lib/server.cc b/src/lib/server.cc
index 0c5792ae0..b9bb825ee 100644
--- a/src/lib/server.cc
+++ b/src/lib/server.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,6 +37,7 @@
#include "dcp_video_frame.h"
#include "config.h"
#include "cross.h"
+#include "player_video_frame.h"
#include "i18n.h"
@@ -75,7 +76,7 @@ Server::process (shared_ptr<Socket> socket, struct timeval& after_read, struct t
uint32_t length = socket->read_uint32 ();
scoped_array<char> buffer (new char[length]);
socket->read (reinterpret_cast<uint8_t*> (buffer.get()), length);
-
+
stringstream s (buffer.get());
shared_ptr<cxml::Document> xml (new cxml::Document ("EncodingRequest"));
xml->read_stream (s);
@@ -85,14 +86,9 @@ Server::process (shared_ptr<Socket> socket, struct timeval& after_read, struct t
return -1;
}
- libdcp::Size size (
- xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
- );
-
- shared_ptr<Image> image (new Image (PIX_FMT_RGB24, size, true));
+ shared_ptr<PlayerVideoFrame> pvf (new PlayerVideoFrame (xml, socket));
- image->read_from_socket (socket);
- DCPVideoFrame dcp_video_frame (image, xml, _log);
+ DCPVideoFrame dcp_video_frame (pvf, xml, _log);
gettimeofday (&after_read, 0);
@@ -103,15 +99,11 @@ Server::process (shared_ptr<Socket> socket, struct timeval& after_read, struct t
try {
encoded->send (socket);
} catch (std::exception& e) {
- _log->log (String::compose (
- "Send failed; frame %1, data size %2, pixel format %3, image size %4x%5, %6 components",
- dcp_video_frame.frame(), encoded->size(), image->pixel_format(), image->size().width, image->size().height, image->components()
- )
- );
+ _log->log (String::compose ("Send failed; frame %1", dcp_video_frame.index()));
throw;
}
- return dcp_video_frame.frame ();
+ return dcp_video_frame.index ();
}
void
diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc
index 1c8f7e3eb..b11ce8be5 100644
--- a/src/lib/transcoder.cc
+++ b/src/lib/transcoder.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,11 +40,11 @@ using boost::weak_ptr;
using boost::dynamic_pointer_cast;
static void
-video_proxy (weak_ptr<Encoder> encoder, shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, bool same)
+video_proxy (weak_ptr<Encoder> encoder, shared_ptr<PlayerVideoFrame> pvf, bool same)
{
shared_ptr<Encoder> e = encoder.lock ();
if (e) {
- e->process_video (image, eyes, conversion, same);
+ e->process_video (pvf, same);
}
}
@@ -66,7 +66,7 @@ Transcoder::Transcoder (shared_ptr<const Film> f, shared_ptr<Job> j)
, _encoder (new Encoder (f, j))
, _finishing (false)
{
- _player->Video.connect (bind (video_proxy, _encoder, _1, _2, _3, _4));
+ _player->Video.connect (bind (video_proxy, _encoder, _1, _2));
_player->Audio.connect (bind (audio_proxy, _encoder, _1));
}
diff --git a/src/lib/types.cc b/src/lib/types.cc
index bc4f5f8d9..83bbf41e4 100644
--- a/src/lib/types.cc
+++ b/src/lib/types.cc
@@ -17,11 +17,16 @@
*/
+#include <libxml++/libxml++.h>
+#include <libcxml/cxml.h>
+#include <libdcp/raw_convert.h>
#include "types.h"
using std::max;
using std::min;
using std::string;
+using boost::shared_ptr;
+using libdcp::raw_convert;
bool operator== (Crop const & a, Crop const & b)
{
@@ -65,3 +70,20 @@ string_to_resolution (string s)
assert (false);
return RESOLUTION_2K;
}
+
+Crop::Crop (shared_ptr<cxml::Node> node)
+{
+ left = node->number_child<int> ("LeftCrop");
+ right = node->number_child<int> ("RightCrop");
+ top = node->number_child<int> ("TopCrop");
+ bottom = node->number_child<int> ("BottomCrop");
+}
+
+void
+Crop::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("LeftCrop")->add_child_text (raw_convert<string> (left));
+ node->add_child("RightCrop")->add_child_text (raw_convert<string> (right));
+ node->add_child("TopCrop")->add_child_text (raw_convert<string> (top));
+ node->add_child("BottomCrop")->add_child_text (raw_convert<string> (bottom));
+}
diff --git a/src/lib/types.h b/src/lib/types.h
index c255bd0d8..3fab302fc 100644
--- a/src/lib/types.h
+++ b/src/lib/types.h
@@ -32,11 +32,19 @@ class SubtitleContent;
class FFmpegContent;
class AudioBuffers;
+namespace cxml {
+ class Node;
+}
+
+namespace xmlpp {
+ class Node;
+}
+
/** The version number of the protocol used to communicate
* with servers. Intended to be bumped when incompatibilities
* are introduced.
*/
-#define SERVER_LINK_VERSION 1
+#define SERVER_LINK_VERSION 2
typedef int64_t Time;
#define TIME_MAX INT64_MAX
@@ -85,6 +93,15 @@ enum Eyes
EYES_COUNT
};
+enum Part
+{
+ PART_LEFT_HALF,
+ PART_RIGHT_HALF,
+ PART_TOP_HALF,
+ PART_BOTTOM_HALF,
+ PART_WHOLE
+};
+
/** @struct Crop
* @brief A description of the crop of an image or video.
*/
@@ -92,6 +109,7 @@ struct Crop
{
Crop () : left (0), right (0), top (0), bottom (0) {}
Crop (int l, int r, int t, int b) : left (l), right (r), top (t), bottom (b) {}
+ Crop (boost::shared_ptr<cxml::Node>);
/** Number of pixels to remove from the left-hand side */
int left;
@@ -116,6 +134,8 @@ struct Crop
return s;
}
+
+ void as_xml (xmlpp::Node *) const;
};
extern bool operator== (Crop const & a, Crop const & b);
diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc
index 783cddafa..40772980f 100644
--- a/src/lib/video_content.cc
+++ b/src/lib/video_content.cc
@@ -161,10 +161,7 @@ VideoContent::as_xml (xmlpp::Node* node) const
node->add_child("VideoHeight")->add_child_text (raw_convert<string> (_video_size.height));
node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
node->add_child("VideoFrameType")->add_child_text (raw_convert<string> (static_cast<int> (_video_frame_type)));
- node->add_child("LeftCrop")->add_child_text (raw_convert<string> (_crop.left));
- node->add_child("RightCrop")->add_child_text (raw_convert<string> (_crop.right));
- node->add_child("TopCrop")->add_child_text (raw_convert<string> (_crop.top));
- node->add_child("BottomCrop")->add_child_text (raw_convert<string> (_crop.bottom));
+ _crop.as_xml (node);
_scale.as_xml (node->add_child("Scale"));
_colour_conversion.as_xml (node->add_child("ColourConversion"));
}
diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc
index 2a33a8c3a..5867ac925 100644
--- a/src/lib/video_decoder.cc
+++ b/src/lib/video_decoder.cc
@@ -34,34 +34,28 @@ VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoCont
}
void
-VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
+VideoDecoder::video (shared_ptr<const ImageProxy> image, bool same, VideoContent::Frame frame)
{
switch (_video_content->video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
- Video (image, EYES_BOTH, same, frame);
+ Video (image, EYES_BOTH, PART_WHOLE, same, frame);
break;
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
- Video (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, same, frame / 2);
+ Video (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, same, frame / 2);
break;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- {
- int const half = image->size().width / 2;
- Video (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, frame);
- Video (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, frame);
+ Video (image, EYES_LEFT, PART_LEFT_HALF, same, frame);
+ Video (image, EYES_RIGHT, PART_RIGHT_HALF, same, frame);
break;
- }
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- {
- int const half = image->size().height / 2;
- Video (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, frame);
- Video (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, frame);
+ Video (image, EYES_LEFT, PART_TOP_HALF, same, frame);
+ Video (image, EYES_RIGHT, PART_BOTTOM_HALF, same, frame);
break;
- }
case VIDEO_FRAME_TYPE_3D_LEFT:
- Video (image, EYES_LEFT, same, frame);
+ Video (image, EYES_LEFT, PART_WHOLE, same, frame);
break;
case VIDEO_FRAME_TYPE_3D_RIGHT:
- Video (image, EYES_RIGHT, same, frame);
+ Video (image, EYES_RIGHT, PART_WHOLE, same, frame);
break;
}
diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h
index 255a038a9..42add42aa 100644
--- a/src/lib/video_decoder.h
+++ b/src/lib/video_decoder.h
@@ -27,7 +27,7 @@
#include "util.h"
class VideoContent;
-class Image;
+class ImageProxy;
class VideoDecoder : public virtual Decoder
{
@@ -42,14 +42,15 @@ public:
/** Emitted when a video frame is ready.
* First parameter is the video image.
* Second parameter is the eye(s) which should see this image.
- * Third parameter is true if the image is the same as the last one that was emitted for this Eyes value.
+ * Third parameter is the part of this image that should be used.
+ * Fourth parameter is true if the image is the same as the last one that was emitted for this Eyes value.
* Fourth parameter is the frame within our source.
*/
- boost::signals2::signal<void (boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame)> Video;
+ boost::signals2::signal<void (boost::shared_ptr<const ImageProxy>, Eyes, Part, bool, VideoContent::Frame)> Video;
protected:
- void video (boost::shared_ptr<const Image>, bool, VideoContent::Frame);
+ void video (boost::shared_ptr<const ImageProxy>, bool, VideoContent::Frame);
boost::shared_ptr<const VideoContent> _video_content;
/** This is in frames without taking 3D into account (e.g. if we are doing 3D alternate,
* this would equal 2 on the left-eye second frame (not 1)).
diff --git a/src/lib/writer.h b/src/lib/writer.h
index 7af79a417..c0699ad44 100644
--- a/src/lib/writer.h
+++ b/src/lib/writer.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/lib/wscript b/src/lib/wscript
index d4231fd30..72e149879 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -35,6 +35,7 @@ sources = """
image_content.cc
image_decoder.cc
image_examiner.cc
+ image_proxy.cc
job.cc
job_manager.cc
kdm.cc
@@ -42,6 +43,7 @@ sources = """
log.cc
piece.cc
player.cc
+ player_video_frame.cc
playlist.cc
ratio.cc
resampler.cc
diff --git a/src/tools/server_test.cc b/src/tools/server_test.cc
index 039088862..896517b27 100644
--- a/src/tools/server_test.cc
+++ b/src/tools/server_test.cc
@@ -34,6 +34,7 @@
#include "lib/log.h"
#include "lib/video_decoder.h"
#include "lib/player.h"
+#include "lib/player_video_frame.h"
using std::cout;
using std::cerr;
@@ -47,10 +48,10 @@ static shared_ptr<FileLog> log_ (new FileLog ("servomatictest.log"));
static int frame = 0;
void
-process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, Time)
+process_video (shared_ptr<PlayerVideoFrame> pvf)
{
- shared_ptr<DCPVideoFrame> local (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
- shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
+ shared_ptr<DCPVideoFrame> local (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
+ shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
cout << "Frame " << frame << ": ";
cout.flush ();
@@ -145,7 +146,7 @@ main (int argc, char* argv[])
shared_ptr<Player> player = film->make_player ();
player->disable_audio ();
- player->Video.connect (boost::bind (process_video, _1, _2, _3, _5));
+ player->Video.connect (boost::bind (process_video, _1));
bool done = false;
while (!done) {
done = player->pass ();
diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc
index ce5eab00e..ed1a2ce41 100644
--- a/src/wx/film_viewer.cc
+++ b/src/wx/film_viewer.cc
@@ -34,6 +34,7 @@
#include "lib/examine_content_job.h"
#include "lib/filter.h"
#include "lib/player.h"
+#include "lib/player_video_frame.h"
#include "lib/video_content.h"
#include "lib/video_decoder.h"
#include "film_viewer.h"
@@ -136,7 +137,7 @@ FilmViewer::set_film (shared_ptr<Film> f)
}
_player->disable_audio ();
- _player->Video.connect (boost::bind (&FilmViewer::process_video, this, _1, _2, _5));
+ _player->Video.connect (boost::bind (&FilmViewer::process_video, this, _1, _3));
_player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1));
calculate_sizes ();
@@ -288,13 +289,13 @@ FilmViewer::check_play_state ()
}
void
-FilmViewer::process_video (shared_ptr<PlayerImage> image, Eyes eyes, Time t)
+FilmViewer::process_video (shared_ptr<PlayerVideoFrame> pvf, Time t)
{
- if (eyes == EYES_RIGHT) {
+ if (pvf->eyes() == EYES_RIGHT) {
return;
}
- _frame = image->image ();
+ _frame = pvf->image ();
_got_frame = true;
set_position_text (t);
diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h
index c99c73440..1e5b6d34d 100644
--- a/src/wx/film_viewer.h
+++ b/src/wx/film_viewer.h
@@ -28,7 +28,7 @@ class wxToggleButton;
class FFmpegPlayer;
class Image;
class RGBPlusAlphaImage;
-class PlayerImage;
+class PlayerVideoFrame;
/** @class FilmViewer
* @brief A wx widget to view a preview of a Film.
@@ -59,7 +59,7 @@ private:
void slider_moved ();
void play_clicked ();
void timer ();
- void process_video (boost::shared_ptr<PlayerImage>, Eyes, Time);
+ void process_video (boost::shared_ptr<PlayerVideoFrame>, Time);
void calculate_sizes ();
void check_play_state ();
void fetch_current_frame_again ();