Add ImageProxy class.
authorCarl Hetherington <cth@carlh.net>
Wed, 14 May 2014 20:06:23 +0000 (21:06 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 14 May 2014 20:06:23 +0000 (21:06 +0100)
16 files changed:
src/lib/ffmpeg_decoder.cc
src/lib/image_decoder.cc
src/lib/image_decoder.h
src/lib/image_proxy.cc [new file with mode: 0644]
src/lib/image_proxy.h [new file with mode: 0644]
src/lib/piece.cc
src/lib/piece.h
src/lib/player.cc
src/lib/player.h
src/lib/player_video_frame.cc
src/lib/player_video_frame.h
src/lib/types.h
src/lib/video_decoder.cc
src/lib/video_decoder.h
src/lib/wscript
test/client_server_test.cc

index c93012608406cab6aeaf8c78414f5215366898df..7a5bf8ba832f51a538ea57775147ba20296a3c06 100644 (file)
@@ -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 {
index a7999c02a86b5270b09dd50fd118c00280ef5761..d33b64cd446086a96c092bf9c9e64e6b8292ee7a 100644 (file)
@@ -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);
 }
 
index c7500243e08091ec3d6669f3c14273dfb93454e4..5b82dd85c161eedd07ccfb0c09cdc90c6dc96708 100644 (file)
@@ -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 (file)
index 0000000..47ac5d3
--- /dev/null
@@ -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 (file)
index 0000000..792fa00
--- /dev/null
@@ -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);
index 3c39ecfb8dc8fac1ac637b4eba13ac3c7ff5157a..494fb17a04f1dca75b3a099ae382b3ef2354e01c 100644 (file)
@@ -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 ())
index 76df909ff361ba8c43e48ee42097071e50f1424e..17b87b884664e49ef3ef787f87f5ce5ffbbe3b03 100644 (file)
 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;
index a50ab3a37ee9669ae0a3788ebe071e0c57a6010f..9f0f380e377c253eb1944e6d268fd7183155450b 100644 (file)
@@ -30,6 +30,7 @@
 #include "playlist.h"
 #include "job.h"
 #include "image.h"
+#include "image_proxy.h"
 #include "ratio.h"
 #include "resampler.h"
 #include "log.h"
@@ -179,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;
@@ -218,6 +220,7 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
                        _video_container_size,
                        _film->scaler(),
                        eyes,
+                       part,
                        content->colour_conversion()
                        )
                );
@@ -421,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));
 
@@ -444,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;
                        }
                }
@@ -523,12 +526,13 @@ Player::set_video_container_size (libdcp::Size s)
        
        _black_frame.reset (
                new PlayerVideoFrame (
-                       im,
+                       shared_ptr<ImageProxy> (new RawImageProxy (im)),
                        Crop(),
                        _video_container_size,
                        _video_container_size,
                        Scaler::from_id ("bicubic"),
                        EYES_BOTH,
+                       PART_WHOLE,
                        ColourConversion ()
                        )
                );
@@ -623,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
index eddd8f6a22528467cd2f2b438e1429808a73d60a..bf6260c0a912260d71a7203a0a6f845e20182db2 100644 (file)
@@ -40,6 +40,7 @@ class Piece;
 class Image;
 class Resampler;
 class PlayerVideoFrame;
+class ImageProxy;
  
 /** @class Player
  *  @brief A class which can `play' a Playlist; emitting its audio and video.
@@ -85,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 ();
index cbe879d10e9ae1eca5b40726a20e945e9df678ca..c96ed3a33d9e14753dcccdbd55aa2a3dfd87a230 100644 (file)
@@ -20,6 +20,7 @@
 #include <libdcp/raw_convert.h>
 #include "player_video_frame.h"
 #include "image.h"
+#include "image_proxy.h"
 #include "scaler.h"
 
 using std::string;
@@ -28,12 +29,13 @@ using boost::shared_ptr;
 using libdcp::raw_convert;
 
 PlayerVideoFrame::PlayerVideoFrame (
-       shared_ptr<const Image> in,
+       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)
@@ -42,6 +44,7 @@ PlayerVideoFrame::PlayerVideoFrame (
        , _out_size (out_size)
        , _scaler (scaler)
        , _eyes (eyes)
+       , _part (part)
        , _colour_conversion (colour_conversion)
 {
 
@@ -55,11 +58,10 @@ PlayerVideoFrame::PlayerVideoFrame (shared_ptr<cxml::Node> node, shared_ptr<Sock
        _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);
 
-       shared_ptr<Image> image (new Image (PIX_FMT_RGB24, libdcp::Size (node->number_child<int> ("InWidth"), node->number_child<int> ("InHeight")), true));
-       image->read_from_socket (socket);
-       _in = image;
+       _in = image_proxy_factory (node->node_child ("In"), socket);
 
        if (node->optional_number_child<int> ("SubtitleX")) {
                
@@ -84,7 +86,27 @@ PlayerVideoFrame::set_subtitle (shared_ptr<const Image> image, Position<int> pos
 shared_ptr<Image>
 PlayerVideoFrame::image () const
 {
-       shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
+       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);
 
@@ -96,17 +118,17 @@ PlayerVideoFrame::image () const
 }
 
 void
-PlayerVideoFrame::add_metadata (xmlpp::Element* node) const
+PlayerVideoFrame::add_metadata (xmlpp::Node* node) const
 {
        _crop.as_xml (node);
-       node->add_child("InWidth")->add_child_text (raw_convert<string> (_in->size().width));
-       node->add_child("InHeight")->add_child_text (raw_convert<string> (_in->size().height));
+       _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));
@@ -119,7 +141,7 @@ PlayerVideoFrame::add_metadata (xmlpp::Element* node) const
 void
 PlayerVideoFrame::send_binary (shared_ptr<Socket> socket) const
 {
-       _in->write_to_socket (socket);
+       _in->send_binary (socket);
        if (_subtitle_image) {
                _subtitle_image->write_to_socket (socket);
        }
index ea7e544819be8c1b822ac9fb8c11b65bf1989845..6461134a9c60649cb106292c1633d4fd36ec3c1d 100644 (file)
@@ -23,6 +23,7 @@
 #include "colour_conversion.h"
 
 class Image;
+class ImageProxy;
 class Scaler;
 class Socket;
 
@@ -33,14 +34,14 @@ class Socket;
 class PlayerVideoFrame
 {
 public:
-       PlayerVideoFrame (boost::shared_ptr<const Image>, Crop, libdcp::Size, libdcp::Size, Scaler const *, Eyes, ColourConversion);
+       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::Element* node) const;
+       void add_metadata (xmlpp::Node* node) const;
        void send_binary (boost::shared_ptr<Socket> socket) const;
 
        Eyes eyes () const {
@@ -52,12 +53,13 @@ public:
        }
 
 private:
-       boost::shared_ptr<const Image> _in;
+       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;
index 8e2384d804cc08df409c2e01fcd2373dc7032fdc..3fab302fc4fe4a26ababa059e4ad3b263a333d16 100644 (file)
@@ -93,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.
  */
index 2a33a8c3a260d78bc2697f80f217e78d635ff1ff..5867ac9257aacc31f8c712039c093f531e636771 100644 (file)
@@ -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;
        }
        
index 255a038a9073f75be2a1c268e11aaf709642072b..42add42aacc547c3be5e2bced87882dfdacecd0b 100644 (file)
@@ -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)).
index 7e2e10e1f3dc4fff2efef8cac150410ca09fc2b0..72e149879196a20873dbfdb1bc698b06a1320d85 100644 (file)
@@ -35,6 +35,7 @@ sources = """
           image_content.cc
           image_decoder.cc
           image_examiner.cc
+          image_proxy.cc
           job.cc
           job_manager.cc
           kdm.cc
index 2b0e96775274d1a231cbb5c10c44f5fc8780016c..c8a2b49efdcd88f83133ec6e25d81cc3175a5225 100644 (file)
@@ -25,6 +25,7 @@
 #include "lib/dcp_video_frame.h"
 #include "lib/scaler.h"
 #include "lib/player_video_frame.h"
+#include "lib/image_proxy.h"
 
 using std::list;
 using boost::shared_ptr;
@@ -71,12 +72,13 @@ BOOST_AUTO_TEST_CASE (client_server_test)
 
        shared_ptr<PlayerVideoFrame> pvf (
                new PlayerVideoFrame (
-                       image,
+                       shared_ptr<ImageProxy> (new RawImageProxy (image)),
                        Crop (),
                        libdcp::Size (1998, 1080),
                        libdcp::Size (1998, 1080),
                        Scaler::from_id ("bicubic"),
                        EYES_BOTH,
+                       PART_WHOLE,
                        ColourConversion ()
                        )
                );