diff options
| author | Carl Hetherington <cth@carlh.net> | 2014-05-20 13:23:26 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2014-05-20 13:23:26 +0100 |
| commit | 39bc73fe192f932ed6695eb87b19de446e8b4f55 (patch) | |
| tree | 4811b0642b7fcd73206b037da4ffb342e90ce6dc /src/lib | |
| parent | bb0a36c3a6bea9cd1ebdde7b8a3a04765e317569 (diff) | |
| parent | 884093edac5f63d26c02d411c73dfb52376ccf8f (diff) | |
Merge master.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/colour_conversion.cc | 1 | ||||
| -rw-r--r-- | src/lib/content_video.h | 8 | ||||
| -rw-r--r-- | src/lib/dcp_video.cc | 79 | ||||
| -rw-r--r-- | src/lib/dcp_video_frame.cc | 115 | ||||
| -rw-r--r-- | src/lib/dcp_video_frame.h | 21 | ||||
| -rw-r--r-- | src/lib/encoder.cc | 28 | ||||
| -rw-r--r-- | src/lib/encoder.h | 6 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 3 | ||||
| -rw-r--r-- | src/lib/image.cc | 23 | ||||
| -rw-r--r-- | src/lib/image.h | 2 | ||||
| -rw-r--r-- | src/lib/image_decoder.cc | 41 | ||||
| -rw-r--r-- | src/lib/image_decoder.h | 2 | ||||
| -rw-r--r-- | src/lib/image_proxy.cc | 161 | ||||
| -rw-r--r-- | src/lib/image_proxy.h | 84 | ||||
| -rw-r--r-- | src/lib/player.cc | 57 | ||||
| -rw-r--r-- | src/lib/player.h | 35 | ||||
| -rw-r--r-- | src/lib/player_video_frame.cc | 146 | ||||
| -rw-r--r-- | src/lib/player_video_frame.h (renamed from src/lib/dcp_video.h) | 39 | ||||
| -rw-r--r-- | src/lib/server.cc | 22 | ||||
| -rw-r--r-- | src/lib/transcoder.cc | 4 | ||||
| -rw-r--r-- | src/lib/types.cc | 22 | ||||
| -rw-r--r-- | src/lib/types.h | 22 | ||||
| -rw-r--r-- | src/lib/video_content.cc | 5 | ||||
| -rw-r--r-- | src/lib/video_decoder.cc | 25 | ||||
| -rw-r--r-- | src/lib/video_decoder.h | 4 | ||||
| -rw-r--r-- | src/lib/wscript | 3 |
26 files changed, 634 insertions, 324 deletions
diff --git a/src/lib/colour_conversion.cc b/src/lib/colour_conversion.cc index 73ee72249..48fd6ed9c 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/content_video.h b/src/lib/content_video.h index 20b5b8dec..a7f73597c 100644 --- a/src/lib/content_video.h +++ b/src/lib/content_video.h @@ -20,7 +20,7 @@ #ifndef DCPOMATIC_CONTENT_VIDEO_H #define DCPOMATIC_CONTENT_VIDEO_H -class Image; +class ImageProxy; /** @class ContentVideo * @brief A frame of video straight out of some content. @@ -32,14 +32,16 @@ public: : eyes (EYES_BOTH) {} - ContentVideo (boost::shared_ptr<const Image> i, Eyes e, VideoFrame f) + ContentVideo (boost::shared_ptr<const ImageProxy> i, Eyes e, Part p, VideoFrame f) : image (i) , eyes (e) + , part (p) , frame (f) {} - boost::shared_ptr<const Image> image; + boost::shared_ptr<const ImageProxy> image; Eyes eyes; + Part part; VideoFrame frame; }; diff --git a/src/lib/dcp_video.cc b/src/lib/dcp_video.cc deleted file mode 100644 index 8ea5cec88..000000000 --- a/src/lib/dcp_video.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - 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 "dcp_video.h" -#include "image.h" - -using boost::shared_ptr; - -/** From ContentVideo: - * @param in Image. - * @param eyes Eye(s) that the Image is for. - * - * From Content: - * @param crop Crop to apply. - * @param inter_size - * @param out_size - * @param scaler Scaler to use. - * @param conversion Colour conversion to use. - * - * @param time DCP time. - */ -DCPVideo::DCPVideo ( - shared_ptr<const Image> in, - Eyes eyes, - Crop crop, - dcp::Size inter_size, - dcp::Size out_size, - Scaler const * scaler, - ColourConversion conversion, - DCPTime time - ) - : _in (in) - , _eyes (eyes) - , _crop (crop) - , _inter_size (inter_size) - , _out_size (out_size) - , _scaler (scaler) - , _conversion (conversion) - , _time (time) -{ - -} - -void -DCPVideo::set_subtitle (PositionImage s) -{ - _subtitle = s; -} - -shared_ptr<Image> -DCPVideo::image (AVPixelFormat format, bool aligned) const -{ - shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned); - - 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/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index d860c3195..d154ba96b 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -43,6 +43,7 @@ #include <boost/asio.hpp> #include <boost/filesystem.hpp> #include <boost/lexical_cast.hpp> +#include <openssl/md5.h> #include <dcp/gamma_lut.h> #include <dcp/xyz_frame.h> #include <dcp/rgb_xyz.h> @@ -59,6 +60,7 @@ #include "image.h" #include "log.h" #include "cross.h" +#include "player_video_frame.h" #include "i18n.h" @@ -73,18 +75,16 @@ using dcp::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, cxml::ConstNodePtr 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)); @@ -120,28 +109,44 @@ DCPVideoFrame::DCPVideoFrame (shared_ptr<const Image> image, cxml::ConstNodePtr shared_ptr<EncodedData> DCPVideoFrame::encode_locally () { - shared_ptr<dcp::GammaLUT> in_lut; - in_lut = dcp::GammaLUT::cache.get (12, _conversion.input_gamma, _conversion.input_gamma_linearised); - + shared_ptr<dcp::GammaLUT> in_lut = dcp::GammaLUT::cache.get ( + 12, _frame->colour_conversion().input_gamma, _frame->colour_conversion().input_gamma_linearised + ); + /* XXX: libdcp should probably use boost */ 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<dcp::XYZFrame> xyz = dcp::rgb_to_xyz ( - _image, + _frame->image(), in_lut, - dcp::GammaLUT::cache.get (16, 1 / _conversion.output_gamma, false), + dcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma, false), 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; } @@ -240,15 +245,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; @@ -279,28 +284,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; } @@ -308,27 +315,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 c51a3f02b..7393efde6 100644 --- a/src/lib/dcp_video_frame.h +++ b/src/lib/dcp_video_frame.h @@ -31,6 +31,7 @@ class Scaler; class Image; class Log; class Subtitle; +class PlayerVideoFrame; /** @class EncodedData * @brief Container for J2K-encoded data. @@ -100,28 +101,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>, cxml::ConstNodePtr, boost::shared_ptr<Log>); + DCPVideoFrame (boost::shared_ptr<const PlayerVideoFrame>, int, int, int, Resolution, boost::shared_ptr<Log>); + DCPVideoFrame (boost::shared_ptr<const PlayerVideoFrame>, cxml::ConstNodePtr, 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 b83cbc10a..2364b67a7 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -35,7 +35,7 @@ #include "writer.h" #include "server_finder.h" #include "player.h" -#include "dcp_video.h" +#include "player_video_frame.h" #include "i18n.h" @@ -123,9 +123,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 ())); @@ -178,7 +178,7 @@ Encoder::frame_done () } void -Encoder::process_video (shared_ptr<DCPVideo> frame) +Encoder::process_video (shared_ptr<PlayerVideoFrame> pvf) { _waker.nudge (); @@ -205,17 +205,15 @@ Encoder::process_video (shared_ptr<DCPVideo> frame) rethrow (); if (_writer->can_fake_write (_video_frames_out)) { - _writer->fake_write (_video_frames_out, frame->eyes ()); + _writer->fake_write (_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 ( - frame->image(PIX_FMT_RGB24, false), + pvf, _video_frames_out, - frame->eyes(), - frame->conversion(), _film->video_frame_rate(), _film->j2k_bandwidth(), _film->resolution(), @@ -226,7 +224,7 @@ Encoder::process_video (shared_ptr<DCPVideo> frame) _condition.notify_all (); } - if (frame->eyes() != EYES_LEFT) { + if (pvf->eyes() != EYES_LEFT) { ++_video_frames_out; } } @@ -280,7 +278,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 (); @@ -306,27 +304,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 6c465f816..ac1d74c57 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 DCPVideo; +class PlayerVideoFrame; /** @class Encoder * @brief Encoder to J2K and WAV for DCP. @@ -69,7 +69,7 @@ public: /** Call with a frame of video. * @param f Video frame. */ - void process_video (boost::shared_ptr<DCPVideo> f); + void process_video (boost::shared_ptr<PlayerVideoFrame> f); /** 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 9ae5f0485..d251a3744 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -41,6 +41,7 @@ extern "C" { #include "filter_graph.h" #include "audio_buffers.h" #include "ffmpeg_content.h" +#include "image_proxy.h" #include "i18n.h" @@ -484,7 +485,7 @@ FFmpegDecoder::decode_video_packet () if (i->second != AV_NOPTS_VALUE) { double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset.seconds (); - video (image, rint (pts * _ffmpeg_content->video_frame_rate ())); + video (shared_ptr<ImageProxy> (new RawImageProxy (image)), rint (pts * _ffmpeg_content->video_frame_rate ())); } else { _log->log ("Dropping frame without PTS"); } diff --git a/src/lib/image.cc b/src/lib/image.cc index 432cfbd54..d4ec6f99a 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> @@ -40,6 +41,7 @@ using std::min; using std::cout; using std::cerr; using std::list; +using std::stringstream; using boost::shared_ptr; using dcp::Size; @@ -662,3 +664,24 @@ merge (list<PositionImage> images) return PositionImage (merged, all.position ()); } + +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 23b88dd76..23c85e92b 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -75,6 +75,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 5de0c8582..9f83d1d89 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" @@ -46,45 +47,13 @@ ImageDecoder::pass () return true; } - if (_image && _image_content->still ()) { - video (_image, _video_position); - ++_video_position; - return false; + if (!_image_content->still() || !_image) { + /* Either we need an image or we are using moving images, so load one */ + _image.reset (new MagickImageProxy (_image_content->path (_image_content->still() ? 0 : _video_position))); } - - 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); - } - - dcp::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; - + video (_image, _video_position); ++_video_position; - return false; } diff --git a/src/lib/image_decoder.h b/src/lib/image_decoder.h index 8d88df3de..242f69477 100644 --- a/src/lib/image_decoder.h +++ b/src/lib/image_decoder.h @@ -40,7 +40,7 @@ private: bool pass (); boost::shared_ptr<const ImageContent> _image_content; - boost::shared_ptr<Image> _image; + boost::shared_ptr<ImageProxy> _image; VideoFrame _video_position; }; diff --git a/src/lib/image_proxy.cc b/src/lib/image_proxy.cc new file mode 100644 index 000000000..c74e846c9 --- /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 <dcp/util.h> +#include <dcp/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) +{ + dcp::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 (dcp::raw_convert<string> (_image->size().width)); + node->add_child("Height")->add_child_text (dcp::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")); + } + + dcp::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/player.cc b/src/lib/player.cc index 75b550093..ab0d8f356 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -34,13 +34,14 @@ #include "playlist.h" #include "job.h" #include "image.h" +#include "image_proxy.h" #include "ratio.h" #include "log.h" #include "scaler.h" #include "render_subtitles.h" -#include "dcp_video.h" #include "config.h" #include "content_video.h" +#include "player_video_frame.h" using std::list; using std::cout; @@ -287,41 +288,41 @@ Player::set_approximate_size () _approximate_size = true; } -shared_ptr<DCPVideo> -Player::black_dcp_video (DCPTime time) const +shared_ptr<PlayerVideoFrame> +Player::black_player_video_frame () const { - return shared_ptr<DCPVideo> ( - new DCPVideo ( - _black_image, - EYES_BOTH, + return shared_ptr<PlayerVideoFrame> ( + new PlayerVideoFrame ( + shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)), Crop (), _video_container_size, _video_container_size, Scaler::from_id ("bicubic"), - Config::instance()->colour_conversions().front().conversion, - time + EYES_BOTH, + PART_WHOLE, + Config::instance()->colour_conversions().front().conversion ) ); } -shared_ptr<DCPVideo> -Player::content_to_dcp ( +shared_ptr<PlayerVideoFrame> +Player::content_to_player_video_frame ( shared_ptr<VideoContent> content, ContentVideo content_video, list<shared_ptr<Piece> > subs, DCPTime time, dcp::Size image_size) const { - shared_ptr<DCPVideo> dcp_video ( - new DCPVideo ( + shared_ptr<PlayerVideoFrame> pvf ( + new PlayerVideoFrame ( content_video.image, - content_video.eyes, content->crop (), image_size, _video_container_size, _film->scaler(), - content->colour_conversion (), - time + content_video.eyes, + content_video.part, + content->colour_conversion () ) ); @@ -356,14 +357,14 @@ Player::content_to_dcp ( } if (!sub_images.empty ()) { - dcp_video->set_subtitle (merge (sub_images)); + pvf->set_subtitle (merge (sub_images)); } - return dcp_video; + return pvf; } -/** @return All DCPVideo at the given time (there may be two frames for 3D) */ -list<shared_ptr<DCPVideo> > +/** @return All PlayerVideoFrames at the given time (there may be two frames for 3D) */ +list<shared_ptr<PlayerVideoFrame> > Player::get_video (DCPTime time, bool accurate) { if (!_have_valid_pieces) { @@ -375,15 +376,15 @@ Player::get_video (DCPTime time, bool accurate) time + DCPTime::from_frames (1, _film->video_frame_rate ()) ); - list<shared_ptr<DCPVideo> > dcp_video; + list<shared_ptr<PlayerVideoFrame> > pvf; if (ov.empty ()) { /* No video content at this time */ - dcp_video.push_back (black_dcp_video (time)); - return dcp_video; + pvf.push_back (black_player_video_frame ()); + return pvf; } - /* Create a DCPVideo from the content's video at this time */ + /* Create a PlayerVideoFrame from the content's video at this time */ shared_ptr<Piece> piece = ov.back (); shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder); @@ -393,8 +394,8 @@ Player::get_video (DCPTime time, bool accurate) list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate); if (content_video.empty ()) { - dcp_video.push_back (black_dcp_video (time)); - return dcp_video; + pvf.push_back (black_player_video_frame ()); + return pvf; } dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ()); @@ -409,10 +410,10 @@ Player::get_video (DCPTime time, bool accurate) time + DCPTime::from_frames (1, _film->video_frame_rate ()) ); - dcp_video.push_back (content_to_dcp (content, *i, subs, time, image_size)); + pvf.push_back (content_to_player_video_frame (content, *i, subs, time, image_size)); } - return dcp_video; + return pvf; } shared_ptr<AudioBuffers> diff --git a/src/lib/player.h b/src/lib/player.h index e47cf53a1..a96c93404 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -40,9 +40,11 @@ class Playlist; class AudioContent; class Piece; class Image; -class DCPVideo; class Decoder; - +class Resampler; +class PlayerVideoFrame; +class ImageProxy; + class PlayerStatistics { public: @@ -75,29 +77,6 @@ public: void dump (boost::shared_ptr<Log>) const; }; -/** @class PlayerImage - * @brief 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, dcp::Size, dcp::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; - dcp::Size _inter_size; - dcp::Size _out_size; - Scaler const * _scaler; - boost::shared_ptr<const Image> _subtitle_image; - Position<int> _subtitle_position; -}; - /** @class Player * @brief A class which can `play' a Playlist. */ @@ -106,7 +85,7 @@ class Player : public boost::enable_shared_from_this<Player>, public boost::nonc public: Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist>); - std::list<boost::shared_ptr<DCPVideo> > get_video (DCPTime time, bool accurate); + std::list<boost::shared_ptr<PlayerVideoFrame> > get_video (DCPTime time, bool accurate); boost::shared_ptr<AudioBuffers> get_audio (DCPTime time, DCPTime length, bool accurate); void set_video_container_size (dcp::Size); @@ -143,8 +122,8 @@ private: VideoFrame dcp_to_content_video (boost::shared_ptr<const Piece> piece, DCPTime t) const; AudioFrame dcp_to_content_audio (boost::shared_ptr<const Piece> piece, DCPTime t) const; ContentTime dcp_to_content_subtitle (boost::shared_ptr<const Piece> piece, DCPTime t) const; - boost::shared_ptr<DCPVideo> black_dcp_video (DCPTime) const; - boost::shared_ptr<DCPVideo> content_to_dcp ( + boost::shared_ptr<PlayerVideoFrame> black_player_video_frame () const; + boost::shared_ptr<PlayerVideoFrame> content_to_player_video_frame ( boost::shared_ptr<VideoContent> content, ContentVideo content_video, std::list<boost::shared_ptr<Piece> > subs, diff --git a/src/lib/player_video_frame.cc b/src/lib/player_video_frame.cc new file mode 100644 index 000000000..4258c6361 --- /dev/null +++ b/src/lib/player_video_frame.cc @@ -0,0 +1,146 @@ +/* + 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 <dcp/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 dcp::raw_convert; + +PlayerVideoFrame::PlayerVideoFrame ( + shared_ptr<const ImageProxy> in, + Crop crop, + dcp::Size inter_size, + dcp::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 = dcp::Size (node->number_child<int> ("InterWidth"), node->number_child<int> ("InterHeight")); + _out_size = dcp::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")); + + _subtitle.image.reset ( + new Image (PIX_FMT_RGBA, dcp::Size (node->number_child<int> ("SubtitleWidth"), node->number_child<int> ("SubtitleHeight")), true) + ); + + _subtitle.image->read_from_socket (socket); + } +} + +void +PlayerVideoFrame::set_subtitle (PositionImage image) +{ + _subtitle = image; +} + +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/dcp_video.h b/src/lib/player_video_frame.h index 75823f31d..225b0a4ba 100644 --- a/src/lib/dcp_video.h +++ b/src/lib/player_video_frame.h @@ -17,49 +17,50 @@ */ -extern "C" { -#include <libavutil/avutil.h> -} #include <boost/shared_ptr.hpp> #include "types.h" -#include "colour_conversion.h" #include "position.h" +#include "colour_conversion.h" #include "position_image.h" class Image; +class ImageProxy; class Scaler; +class Socket; -/** @class DCPVideo - * - * A ContentVideo image with: - * - content parameters (crop, scaling, colour conversion) - * - merged content (subtitles) - * and with its time converted from a ContentTime to a DCPTime. +/** 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 DCPVideo +class PlayerVideoFrame { public: - DCPVideo (boost::shared_ptr<const Image>, Eyes eyes, Crop, dcp::Size, dcp::Size, Scaler const *, ColourConversion conversion, DCPTime time); + PlayerVideoFrame (boost::shared_ptr<const ImageProxy>, Crop, dcp::Size, dcp::Size, Scaler const *, Eyes, Part, ColourConversion); + PlayerVideoFrame (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>); void set_subtitle (PositionImage); - boost::shared_ptr<Image> image (AVPixelFormat, bool) const; + + 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 conversion () const { - return _conversion; + ColourConversion colour_conversion () const { + return _colour_conversion; } private: - boost::shared_ptr<const Image> _in; - Eyes _eyes; + boost::shared_ptr<const ImageProxy> _in; Crop _crop; dcp::Size _inter_size; dcp::Size _out_size; Scaler const * _scaler; - ColourConversion _conversion; - DCPTime _time; + Eyes _eyes; + Part _part; + ColourConversion _colour_conversion; PositionImage _subtitle; }; diff --git a/src/lib/server.cc b/src/lib/server.cc index 6bcff7e6e..d72b7e502 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; } - dcp::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 cc41b4256..a4cd36a4f 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -61,8 +61,8 @@ Transcoder::go () DCPTime const frame = DCPTime::from_frames (1, _film->video_frame_rate ()); for (DCPTime t; t < _film->length(); t += frame) { - list<shared_ptr<DCPVideo> > v = _player->get_video (t, true); - for (list<shared_ptr<DCPVideo> >::const_iterator i = v.begin(); i != v.end(); ++i) { + list<shared_ptr<PlayerVideoFrame> > v = _player->get_video (t, true); + for (list<shared_ptr<PlayerVideoFrame> >::const_iterator i = v.begin(); i != v.end(); ++i) { _encoder->process_video (*i); } _encoder->process_audio (_player->get_audio (t, frame, true)); 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 35c7a91f9..e858d1e1f 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -34,11 +34,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 std::vector<boost::shared_ptr<Content> > ContentList; typedef std::vector<boost::shared_ptr<VideoContent> > VideoContentList; @@ -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 a67a1777e..9c8ecf0bb 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 1b6da8a91..43b1049cc 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -116,7 +116,7 @@ VideoDecoder::get_video (VideoFrame frame, bool accurate) /** Called by subclasses when they have a video frame ready */ void -VideoDecoder::video (shared_ptr<const Image> image, VideoFrame frame) +VideoDecoder::video (shared_ptr<const ImageProxy> image, VideoFrame frame) { /* We should not receive the same thing twice */ assert (_decoded_video.empty() || frame != _decoded_video.back().frame); @@ -132,6 +132,7 @@ VideoDecoder::video (shared_ptr<const Image> image, VideoFrame frame) ContentVideo ( _decoded_video.back().image, _decoded_video.back().eyes, + _decoded_video.back().part, _decoded_video.back().frame + 1 ) ); @@ -139,30 +140,24 @@ VideoDecoder::video (shared_ptr<const Image> image, VideoFrame frame) switch (_video_content->video_frame_type ()) { case VIDEO_FRAME_TYPE_2D: - _decoded_video.push_back (ContentVideo (image, EYES_BOTH, frame)); + _decoded_video.push_back (ContentVideo (image, EYES_BOTH, PART_WHOLE, frame)); break; case VIDEO_FRAME_TYPE_3D_ALTERNATE: - _decoded_video.push_back (ContentVideo (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, frame)); + _decoded_video.push_back (ContentVideo (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, frame)); break; case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT: - { - int const half = image->size().width / 2; - _decoded_video.push_back (ContentVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, frame)); - _decoded_video.push_back (ContentVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, frame)); + _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_LEFT_HALF, frame)); + _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_RIGHT_HALF, frame)); break; - } case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM: - { - int const half = image->size().height / 2; - _decoded_video.push_back (ContentVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, frame)); - _decoded_video.push_back (ContentVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, frame)); + _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_TOP_HALF, frame)); + _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_BOTTOM_HALF, frame)); break; - } case VIDEO_FRAME_TYPE_3D_LEFT: - _decoded_video.push_back (ContentVideo (image, EYES_LEFT, frame)); + _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_WHOLE, frame)); break; case VIDEO_FRAME_TYPE_3D_RIGHT: - _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, frame)); + _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_WHOLE, frame)); break; default: assert (false); diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 145baa40b..2c0028fd1 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -32,7 +32,7 @@ #include "content_video.h" class VideoContent; -class Image; +class ImageProxy; /** @class VideoDecoder * @brief Parent for classes which decode video. @@ -55,7 +55,7 @@ public: protected: void seek (ContentTime time, bool accurate); - void video (boost::shared_ptr<const Image>, VideoFrame frame); + void video (boost::shared_ptr<const ImageProxy>, VideoFrame frame); std::list<ContentVideo> decoded_video (VideoFrame frame); boost::shared_ptr<const VideoContent> _video_content; diff --git a/src/lib/wscript b/src/lib/wscript index 433f50b3f..8f26c53c6 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -17,7 +17,6 @@ sources = """ cross.cc dci_metadata.cc dcp_content_type.cc - dcp_video.cc dcp_video_frame.cc dcpomatic_time.cc dolby_cp750.cc @@ -38,12 +37,14 @@ sources = """ image_content.cc image_decoder.cc image_examiner.cc + image_proxy.cc job.cc job_manager.cc kdm.cc json_server.cc log.cc player.cc + player_video_frame.cc playlist.cc ratio.cc render_subtitles.cc |
