From 8f7d0cf115980cb357bc3da410842503930e66b8 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 14 May 2014 10:05:56 +0100 Subject: Rename PlayerImage to PlayerVideoFrame and give it its own file. --- src/lib/encoder.cc | 5 ++-- src/lib/encoder.h | 6 ++--- src/lib/player.cc | 44 +++---------------------------- src/lib/player.h | 27 +++---------------- src/lib/player_video_frame.cc | 60 +++++++++++++++++++++++++++++++++++++++++++ src/lib/player_video_frame.h | 48 ++++++++++++++++++++++++++++++++++ src/lib/transcoder.cc | 4 +-- src/lib/wscript | 1 + 8 files changed, 124 insertions(+), 71 deletions(-) create mode 100644 src/lib/player_video_frame.cc create mode 100644 src/lib/player_video_frame.h (limited to 'src/lib') diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 8e8da6229..bb2fe6822 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington 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" @@ -179,7 +180,7 @@ Encoder::frame_done () } void -Encoder::process_video (shared_ptr image, Eyes eyes, ColourConversion conversion, bool same) +Encoder::process_video (shared_ptr image, Eyes eyes, ColourConversion conversion, bool same) { _waker.nudge (); diff --git a/src/lib/encoder.h b/src/lib/encoder.h index e0ee2d414..5cdda459d 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington 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. @@ -70,7 +70,7 @@ public: * @param i Video frame image. * @param same true if i is the same as the last time we were called. */ - void process_video (boost::shared_ptr i, Eyes eyes, ColourConversion, bool same); + void process_video (boost::shared_ptr i, Eyes eyes, ColourConversion, bool same); /** Call with some audio data */ void process_audio (boost::shared_ptr); diff --git a/src/lib/player.cc b/src/lib/player.cc index 1bb2e7cf4..eb4a3d3a5 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -34,6 +34,7 @@ #include "resampler.h" #include "log.h" #include "scaler.h" +#include "player_video_frame.h" using std::list; using std::cout; @@ -209,8 +210,8 @@ Player::process_video (weak_ptr weak_piece, shared_ptr 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 pi ( - new PlayerImage ( + shared_ptr pi ( + new PlayerVideoFrame ( image, content->crop(), image_size, @@ -519,7 +520,7 @@ Player::set_video_container_size (libdcp::Size s) im->make_black (); _black_frame.reset ( - new PlayerImage ( + new PlayerVideoFrame ( im, Crop(), _video_container_size, @@ -625,40 +626,3 @@ Player::repeat_last_video () return true; } - -PlayerImage::PlayerImage ( - shared_ptr 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 image, Position pos) -{ - _subtitle_image = image; - _subtitle_position = pos; -} - -shared_ptr -PlayerImage::image () -{ - shared_ptr out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false); - - Position 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..8f7d9a218 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -39,28 +39,7 @@ 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, Crop, libdcp::Size, libdcp::Size, Scaler const *); - - void set_subtitle (boost::shared_ptr, Position); - - boost::shared_ptr image (); - -private: - boost::shared_ptr _in; - Crop _crop; - libdcp::Size _inter_size; - libdcp::Size _out_size; - Scaler const * _scaler; - boost::shared_ptr _subtitle_image; - Position _subtitle_position; -}; +class PlayerVideoFrame; /** @class Player * @brief A class which can `play' a Playlist; emitting its audio and video. @@ -91,7 +70,7 @@ public: * Fourth parameter is true if the image is the same as the last one that was emitted. * Fifth parameter is the time. */ - boost::signals2::signal, Eyes, ColourConversion, bool, Time)> Video; + boost::signals2::signal, Eyes, ColourConversion, bool, Time)> Video; /** Emitted when some audio data is ready */ boost::signals2::signal, Time)> Audio; @@ -140,7 +119,7 @@ private: AudioMerger _audio_merger; libdcp::Size _video_container_size; - boost::shared_ptr _black_frame; + boost::shared_ptr _black_frame; std::map, boost::shared_ptr > _resamplers; std::list _subtitles; diff --git a/src/lib/player_video_frame.cc b/src/lib/player_video_frame.cc new file mode 100644 index 000000000..1c456a253 --- /dev/null +++ b/src/lib/player_video_frame.cc @@ -0,0 +1,60 @@ +/* + Copyright (C) 2013-2014 Carl Hetherington + + 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 "player_video_frame.h" +#include "image.h" + +using boost::shared_ptr; + +PlayerVideoFrame::PlayerVideoFrame ( + shared_ptr 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 +PlayerVideoFrame::set_subtitle (shared_ptr image, Position pos) +{ + _subtitle_image = image; + _subtitle_position = pos; +} + +shared_ptr +PlayerVideoFrame::image () +{ + shared_ptr out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false); + + Position 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_video_frame.h b/src/lib/player_video_frame.h new file mode 100644 index 000000000..51ec7664f --- /dev/null +++ b/src/lib/player_video_frame.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2013-2014 Carl Hetherington + + 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 +#include "types.h" +#include "position.h" + +class Image; +class Scaler; + +/** 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, Crop, libdcp::Size, libdcp::Size, Scaler const *); + + void set_subtitle (boost::shared_ptr, Position); + + boost::shared_ptr image (); + +private: + boost::shared_ptr _in; + Crop _crop; + libdcp::Size _inter_size; + libdcp::Size _out_size; + Scaler const * _scaler; + boost::shared_ptr _subtitle_image; + Position _subtitle_position; +}; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index 1c8f7e3eb..cd729fc22 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington 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,7 +40,7 @@ using boost::weak_ptr; using boost::dynamic_pointer_cast; static void -video_proxy (weak_ptr encoder, shared_ptr image, Eyes eyes, ColourConversion conversion, bool same) +video_proxy (weak_ptr encoder, shared_ptr image, Eyes eyes, ColourConversion conversion, bool same) { shared_ptr e = encoder.lock (); if (e) { diff --git a/src/lib/wscript b/src/lib/wscript index d4231fd30..7e2e10e1f 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -42,6 +42,7 @@ sources = """ log.cc piece.cc player.cc + player_video_frame.cc playlist.cc ratio.cc resampler.cc -- cgit v1.2.3 From 5c09a82e483593fb37da0cad20679be735887613 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 14 May 2014 11:27:17 +0100 Subject: Move Eyes and ColourConversion into PlayerVideoFrame. --- src/lib/encoder.cc | 16 ++++++++-------- src/lib/encoder.h | 6 +++--- src/lib/player.cc | 12 ++++++++---- src/lib/player.h | 8 +++----- src/lib/player_video_frame.cc | 6 +++++- src/lib/player_video_frame.h | 13 ++++++++++++- src/lib/transcoder.cc | 6 +++--- src/tools/server_test.cc | 8 ++++---- src/wx/film_viewer.cc | 8 ++++---- src/wx/film_viewer.h | 2 +- test/play_test.cc | 4 ++-- 11 files changed, 53 insertions(+), 36 deletions(-) (limited to 'src/lib') diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index bb2fe6822..8ae402797 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -180,7 +180,7 @@ Encoder::frame_done () } void -Encoder::process_video (shared_ptr image, Eyes eyes, ColourConversion conversion, bool same) +Encoder::process_video (shared_ptr pvf, bool same) { _waker.nudge (); @@ -207,28 +207,28 @@ Encoder::process_video (shared_ptr image, Eyes eyes, ColourCon 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 ( new DCPVideoFrame ( - image->image(), _video_frames_out, eyes, conversion, _film->video_frame_rate(), + pvf->image(), _video_frames_out, pvf->eyes(), pvf->colour_conversion(), _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; } } diff --git a/src/lib/encoder.h b/src/lib/encoder.h index 5cdda459d..a8ee220aa 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -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 i, Eyes eyes, ColourConversion, bool same); + void process_video (boost::shared_ptr pvf, bool same); /** Call with some audio data */ void process_audio (boost::shared_ptr); diff --git a/src/lib/player.cc b/src/lib/player.cc index eb4a3d3a5..a50ab3a37 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -216,7 +216,9 @@ Player::process_video (weak_ptr weak_piece, shared_ptr image content->crop(), image_size, _video_container_size, - _film->scaler() + _film->scaler(), + eyes, + content->colour_conversion() ) ); @@ -252,7 +254,7 @@ Player::process_video (weak_ptr weak_piece, shared_ptr 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()); @@ -525,7 +527,9 @@ Player::set_video_container_size (libdcp::Size s) Crop(), _video_container_size, _video_container_size, - Scaler::from_id ("bicubic") + Scaler::from_id ("bicubic"), + EYES_BOTH, + ColourConversion () ) ); } @@ -560,7 +564,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; } diff --git a/src/lib/player.h b/src/lib/player.h index 8f7d9a218..eddd8f6a2 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -65,12 +65,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, Eyes, ColourConversion, bool, Time)> Video; + boost::signals2::signal, bool, Time)> Video; /** Emitted when some audio data is ready */ boost::signals2::signal, Time)> Audio; diff --git a/src/lib/player_video_frame.cc b/src/lib/player_video_frame.cc index 1c456a253..571c18b6e 100644 --- a/src/lib/player_video_frame.cc +++ b/src/lib/player_video_frame.cc @@ -27,13 +27,17 @@ PlayerVideoFrame::PlayerVideoFrame ( Crop crop, libdcp::Size inter_size, libdcp::Size out_size, - Scaler const * scaler + Scaler const * scaler, + Eyes eyes, + ColourConversion colour_conversion ) : _in (in) , _crop (crop) , _inter_size (inter_size) , _out_size (out_size) , _scaler (scaler) + , _eyes (eyes) + , _colour_conversion (colour_conversion) { } diff --git a/src/lib/player_video_frame.h b/src/lib/player_video_frame.h index 51ec7664f..42d345c9a 100644 --- a/src/lib/player_video_frame.h +++ b/src/lib/player_video_frame.h @@ -20,6 +20,7 @@ #include #include "types.h" #include "position.h" +#include "colour_conversion.h" class Image; class Scaler; @@ -31,18 +32,28 @@ class Scaler; class PlayerVideoFrame { public: - PlayerVideoFrame (boost::shared_ptr, Crop, libdcp::Size, libdcp::Size, Scaler const *); + PlayerVideoFrame (boost::shared_ptr, Crop, libdcp::Size, libdcp::Size, Scaler const *, Eyes, ColourConversion); void set_subtitle (boost::shared_ptr, Position); boost::shared_ptr image (); + Eyes eyes () const { + return _eyes; + } + + ColourConversion colour_conversion () const { + return _colour_conversion; + } + private: boost::shared_ptr _in; Crop _crop; libdcp::Size _inter_size; libdcp::Size _out_size; Scaler const * _scaler; + Eyes _eyes; + ColourConversion _colour_conversion; boost::shared_ptr _subtitle_image; Position _subtitle_position; }; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index cd729fc22..b11ce8be5 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -40,11 +40,11 @@ using boost::weak_ptr; using boost::dynamic_pointer_cast; static void -video_proxy (weak_ptr encoder, shared_ptr image, Eyes eyes, ColourConversion conversion, bool same) +video_proxy (weak_ptr encoder, shared_ptr pvf, bool same) { shared_ptr 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 f, shared_ptr 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/tools/server_test.cc b/src/tools/server_test.cc index 48d817cfe..6a1e2fa41 100644 --- a/src/tools/server_test.cc +++ b/src/tools/server_test.cc @@ -48,10 +48,10 @@ static shared_ptr log_ (new FileLog ("servomatictest.log")); static int frame = 0; void -process_video (shared_ptr image, Eyes eyes, ColourConversion conversion, Time) +process_video (shared_ptr pvf) { - shared_ptr local (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); - shared_ptr remote (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); + shared_ptr local (new DCPVideoFrame (pvf->image(), frame, pvf->eyes(), pvf->colour_conversion(), film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); + shared_ptr remote (new DCPVideoFrame (pvf->image(), frame, pvf->eyes(), pvf->colour_conversion(), film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); cout << "Frame " << frame << ": "; cout.flush (); @@ -146,7 +146,7 @@ main (int argc, char* argv[]) shared_ptr 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 79f46238c..ed1a2ce41 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -137,7 +137,7 @@ FilmViewer::set_film (shared_ptr 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 (); @@ -289,13 +289,13 @@ FilmViewer::check_play_state () } void -FilmViewer::process_video (shared_ptr image, Eyes eyes, Time t) +FilmViewer::process_video (shared_ptr 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 be8fd9f4e..1e5b6d34d 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -59,7 +59,7 @@ private: void slider_moved (); void play_clicked (); void timer (); - void process_video (boost::shared_ptr, Eyes, Time); + void process_video (boost::shared_ptr, Time); void calculate_sizes (); void check_play_state (); void fetch_current_frame_again (); diff --git a/test/play_test.cc b/test/play_test.cc index a0ad494d1..1ba2e7d88 100644 --- a/test/play_test.cc +++ b/test/play_test.cc @@ -44,10 +44,10 @@ public: PlayerWrapper (shared_ptr p) : _player (p) { - _player->Video.connect (bind (&PlayerWrapper::process_video, this, _1, _2, _5)); + _player->Video.connect (bind (&PlayerWrapper::process_video, this, _1, _3)); } - void process_video (shared_ptr i, bool, Time t) + void process_video (shared_ptr i, Time t) { Video v; v.content = _player->_last_video; -- cgit v1.2.3 From 21e8238484af35ac207b01defe406e73445632be Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 14 May 2014 19:27:04 +0100 Subject: Make DCPVideoFrame use PlayerVideoFrame to store its image. --- src/lib/colour_conversion.cc | 1 + src/lib/dcp_video_frame.cc | 114 ++++++++++++++++++++---------------------- src/lib/dcp_video_frame.h | 23 ++++----- src/lib/encoder.cc | 18 +++---- src/lib/image.cc | 23 +++++++++ src/lib/image.h | 2 + src/lib/player_video_frame.cc | 64 +++++++++++++++++++++++- src/lib/player_video_frame.h | 7 ++- src/lib/server.cc | 22 +++----- src/lib/types.cc | 22 ++++++++ src/lib/types.h | 13 ++++- src/lib/video_content.cc | 5 +- src/lib/writer.h | 2 +- src/tools/server_test.cc | 4 +- test/client_server_test.cc | 22 ++++++-- 15 files changed, 231 insertions(+), 111 deletions(-) (limited to 'src/lib') 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 #include #include +#include #include #include #include @@ -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 image, int f, Eyes eyes, ColourConversion c, int dcp_fps, int bw, Resolution r, shared_ptr l + shared_ptr frame, int index, int dcp_fps, int bw, Resolution r, shared_ptr 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 image, shared_ptr node, shared_ptr log) - : _image (image) +DCPVideoFrame::DCPVideoFrame (shared_ptr frame, shared_ptr node, shared_ptr log) + : _frame (frame) , _log (log) { - _frame = node->number_child ("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 ("Index"); _frames_per_second = node->number_child ("FramesPerSecond"); _j2k_bandwidth = node->number_child ("J2KBandwidth"); _resolution = Resolution (node->optional_number_child("Resolution").get_value_or (RESOLUTION_2K)); @@ -121,10 +110,10 @@ shared_ptr DCPVideoFrame::encode_locally () { shared_ptr 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 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 (SERVER_LINK_VERSION)); - root->add_child("Width")->add_child_text (raw_convert (_image->size().width)); - root->add_child("Height")->add_child_text (raw_convert (_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 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 (_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 (_index)); el->add_child("FramesPerSecond")->add_child_text (raw_convert (_frames_per_second)); el->add_child("J2KBandwidth")->add_child_text (raw_convert (_j2k_bandwidth)); el->add_child("Resolution")->add_child_text (raw_convert (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 + Copyright (C) 2012-2014 Carl Hetherington 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, int, Eyes, ColourConversion, int, int, Resolution, boost::shared_ptr); - DCPVideoFrame (boost::shared_ptr, boost::shared_ptr, boost::shared_ptr); + DCPVideoFrame (boost::shared_ptr, int, int, int, Resolution, boost::shared_ptr); + DCPVideoFrame (boost::shared_ptr, boost::shared_ptr, boost::shared_ptr); boost::shared_ptr encode_locally (); boost::shared_ptr 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 _image; - int _frame; ///< frame index within the DCP's intrinsic duration - Eyes _eyes; - ColourConversion _conversion; + boost::shared_ptr _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 8ae402797..4fc2d7f81 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -125,9 +125,9 @@ Encoder::process_end () */ for (list >::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 ())); @@ -219,7 +219,7 @@ Encoder::process_video (shared_ptr pvf, bool same) TIMING ("adding to queue of %1", _queue.size ()); _queue.push_back (shared_ptr ( new DCPVideoFrame ( - pvf->image(), _video_frames_out, pvf->eyes(), pvf->colour_conversion(), _film->video_frame_rate(), + pvf, _video_frames_out, _film->video_frame_rate(), _film->j2k_bandwidth(), _film->resolution(), _film->log() ) )); @@ -282,7 +282,7 @@ try TIMING ("encoder thread %1 wakes with queue of %2", boost::this_thread::get_id(), _queue.size()); shared_ptr 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 (); @@ -308,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/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 +#include extern "C" { #include #include @@ -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/player_video_frame.cc b/src/lib/player_video_frame.cc index 571c18b6e..cbe879d10 100644 --- a/src/lib/player_video_frame.cc +++ b/src/lib/player_video_frame.cc @@ -17,10 +17,15 @@ */ +#include #include "player_video_frame.h" #include "image.h" +#include "scaler.h" +using std::string; +using std::cout; using boost::shared_ptr; +using libdcp::raw_convert; PlayerVideoFrame::PlayerVideoFrame ( shared_ptr in, @@ -42,6 +47,33 @@ PlayerVideoFrame::PlayerVideoFrame ( } +PlayerVideoFrame::PlayerVideoFrame (shared_ptr node, shared_ptr socket) +{ + _crop = Crop (node); + + _inter_size = libdcp::Size (node->number_child ("InterWidth"), node->number_child ("InterHeight")); + _out_size = libdcp::Size (node->number_child ("OutWidth"), node->number_child ("OutHeight")); + _scaler = Scaler::from_id (node->string_child ("Scaler")); + _eyes = (Eyes) node->number_child ("Eyes"); + _colour_conversion = ColourConversion (node); + + shared_ptr image (new Image (PIX_FMT_RGB24, libdcp::Size (node->number_child ("InWidth"), node->number_child ("InHeight")), true)); + image->read_from_socket (socket); + _in = image; + + if (node->optional_number_child ("SubtitleX")) { + + _subtitle_position = Position (node->number_child ("SubtitleX"), node->number_child ("SubtitleY")); + + shared_ptr image ( + new Image (PIX_FMT_RGBA, libdcp::Size (node->number_child ("SubtitleWidth"), node->number_child ("SubtitleHeight")), true) + ); + + image->read_from_socket (socket); + _subtitle_image = image; + } +} + void PlayerVideoFrame::set_subtitle (shared_ptr image, Position pos) { @@ -50,7 +82,7 @@ PlayerVideoFrame::set_subtitle (shared_ptr image, Position pos } shared_ptr -PlayerVideoFrame::image () +PlayerVideoFrame::image () const { shared_ptr out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false); @@ -62,3 +94,33 @@ PlayerVideoFrame::image () return out; } + +void +PlayerVideoFrame::add_metadata (xmlpp::Element* node) const +{ + _crop.as_xml (node); + node->add_child("InWidth")->add_child_text (raw_convert (_in->size().width)); + node->add_child("InHeight")->add_child_text (raw_convert (_in->size().height)); + node->add_child("InterWidth")->add_child_text (raw_convert (_inter_size.width)); + node->add_child("InterHeight")->add_child_text (raw_convert (_inter_size.height)); + node->add_child("OutWidth")->add_child_text (raw_convert (_out_size.width)); + node->add_child("OutHeight")->add_child_text (raw_convert (_out_size.height)); + node->add_child("Scaler")->add_child_text (_scaler->id ()); + node->add_child("Eyes")->add_child_text (raw_convert (_eyes)); + _colour_conversion.as_xml (node); + if (_subtitle_image) { + node->add_child ("SubtitleWidth")->add_child_text (raw_convert (_subtitle_image->size().width)); + node->add_child ("SubtitleHeight")->add_child_text (raw_convert (_subtitle_image->size().height)); + node->add_child ("SubtitleX")->add_child_text (raw_convert (_subtitle_position.x)); + node->add_child ("SubtitleY")->add_child_text (raw_convert (_subtitle_position.y)); + } +} + +void +PlayerVideoFrame::send_binary (shared_ptr socket) const +{ + _in->write_to_socket (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 index 42d345c9a..ea7e54481 100644 --- a/src/lib/player_video_frame.h +++ b/src/lib/player_video_frame.h @@ -24,6 +24,7 @@ class Image; 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, @@ -33,10 +34,14 @@ class PlayerVideoFrame { public: PlayerVideoFrame (boost::shared_ptr, Crop, libdcp::Size, libdcp::Size, Scaler const *, Eyes, ColourConversion); + PlayerVideoFrame (boost::shared_ptr, boost::shared_ptr); void set_subtitle (boost::shared_ptr, Position); - boost::shared_ptr image (); + boost::shared_ptr image () const; + + void add_metadata (xmlpp::Element* node) const; + void send_binary (boost::shared_ptr socket) const; Eyes eyes () const { return _eyes; 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 + Copyright (C) 2012-2014 Carl Hetherington 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, struct timeval& after_read, struct t uint32_t length = socket->read_uint32 (); scoped_array buffer (new char[length]); socket->read (reinterpret_cast (buffer.get()), length); - + stringstream s (buffer.get()); shared_ptr xml (new cxml::Document ("EncodingRequest")); xml->read_stream (s); @@ -85,14 +86,9 @@ Server::process (shared_ptr socket, struct timeval& after_read, struct t return -1; } - libdcp::Size size ( - xml->number_child ("Width"), xml->number_child ("Height") - ); - - shared_ptr image (new Image (PIX_FMT_RGB24, size, true)); + shared_ptr 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, 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/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 +#include +#include #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 node) +{ + left = node->number_child ("LeftCrop"); + right = node->number_child ("RightCrop"); + top = node->number_child ("TopCrop"); + bottom = node->number_child ("BottomCrop"); +} + +void +Crop::as_xml (xmlpp::Node* node) const +{ + node->add_child("LeftCrop")->add_child_text (raw_convert (left)); + node->add_child("RightCrop")->add_child_text (raw_convert (right)); + node->add_child("TopCrop")->add_child_text (raw_convert (top)); + node->add_child("BottomCrop")->add_child_text (raw_convert (bottom)); +} diff --git a/src/lib/types.h b/src/lib/types.h index c255bd0d8..8e2384d80 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 @@ -92,6 +100,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); /** Number of pixels to remove from the left-hand side */ int left; @@ -116,6 +125,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 (_video_size.height)); node->add_child("VideoFrameRate")->add_child_text (raw_convert (_video_frame_rate)); node->add_child("VideoFrameType")->add_child_text (raw_convert (static_cast (_video_frame_type))); - node->add_child("LeftCrop")->add_child_text (raw_convert (_crop.left)); - node->add_child("RightCrop")->add_child_text (raw_convert (_crop.right)); - node->add_child("TopCrop")->add_child_text (raw_convert (_crop.top)); - node->add_child("BottomCrop")->add_child_text (raw_convert (_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/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 + Copyright (C) 2012-2014 Carl Hetherington 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/tools/server_test.cc b/src/tools/server_test.cc index 6a1e2fa41..896517b27 100644 --- a/src/tools/server_test.cc +++ b/src/tools/server_test.cc @@ -50,8 +50,8 @@ static int frame = 0; void process_video (shared_ptr pvf) { - shared_ptr local (new DCPVideoFrame (pvf->image(), frame, pvf->eyes(), pvf->colour_conversion(), film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); - shared_ptr remote (new DCPVideoFrame (pvf->image(), frame, pvf->eyes(), pvf->colour_conversion(), film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); + shared_ptr local (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); + shared_ptr remote (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_)); cout << "Frame " << frame << ": "; cout.flush (); diff --git a/test/client_server_test.cc b/test/client_server_test.cc index 1ad156ae3..2b0e96775 100644 --- a/test/client_server_test.cc +++ b/test/client_server_test.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington 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 @@ -23,6 +23,8 @@ #include "lib/image.h" #include "lib/cross.h" #include "lib/dcp_video_frame.h" +#include "lib/scaler.h" +#include "lib/player_video_frame.h" using std::list; using boost::shared_ptr; @@ -67,16 +69,26 @@ BOOST_AUTO_TEST_CASE (client_server_test) p += sub_image->stride()[0]; } -// shared_ptr subtitle (new Subtitle (Position (50, 60), sub_image)); + shared_ptr pvf ( + new PlayerVideoFrame ( + image, + Crop (), + libdcp::Size (1998, 1080), + libdcp::Size (1998, 1080), + Scaler::from_id ("bicubic"), + EYES_BOTH, + ColourConversion () + ) + ); + + pvf->set_subtitle (sub_image, Position (50, 60)); shared_ptr log (new FileLog ("build/test/client_server_test.log")); shared_ptr frame ( new DCPVideoFrame ( - image, + pvf, 0, - EYES_BOTH, - ColourConversion (), 24, 200000000, RESOLUTION_2K, -- cgit v1.2.3 From 317222764debbaf1ac96d256df24c00af85c1b6a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 14 May 2014 21:06:23 +0100 Subject: Add ImageProxy class. --- src/lib/ffmpeg_decoder.cc | 5 +- src/lib/image_decoder.cc | 30 +------- src/lib/image_decoder.h | 2 +- src/lib/image_proxy.cc | 161 ++++++++++++++++++++++++++++++++++++++++++ src/lib/image_proxy.h | 84 ++++++++++++++++++++++ src/lib/piece.cc | 1 + src/lib/piece.h | 5 +- src/lib/player.cc | 13 ++-- src/lib/player.h | 3 +- src/lib/player_video_frame.cc | 40 ++++++++--- src/lib/player_video_frame.h | 8 ++- src/lib/types.h | 9 +++ src/lib/video_decoder.cc | 24 +++---- src/lib/video_decoder.h | 9 +-- src/lib/wscript | 1 + test/client_server_test.cc | 4 +- 16 files changed, 329 insertions(+), 70 deletions(-) create mode 100644 src/lib/image_proxy.cc create mode 100644 src/lib/image_proxy.h (limited to 'src/lib') 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 (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 (new RawImageProxy (image)), false, _video_position); } } else { 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 _image_content; - boost::shared_ptr _image; + boost::shared_ptr _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 + + 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 +#include +#include +#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) +{ + +} + +RawImageProxy::RawImageProxy (shared_ptr xml, shared_ptr socket) +{ + libdcp::Size size ( + xml->number_child ("Width"), xml->number_child ("Height") + ); + + _image.reset (new Image (PIX_FMT_RGB24, size, true)); + _image->read_from_socket (socket); +} + +shared_ptr +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 (_image->size().width)); + node->add_child("Height")->add_child_text (libdcp::raw_convert (_image->size().height)); +} + +void +RawImageProxy::send_binary (shared_ptr 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, shared_ptr 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 +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) const +{ + socket->write (_blob.length ()); + socket->write ((uint8_t *) _blob.data (), _blob.length ()); +} + +shared_ptr +image_proxy_factory (shared_ptr xml, shared_ptr socket) +{ + if (xml->string_child("Type") == N_("Raw")) { + return shared_ptr (new RawImageProxy (xml, socket)); + } else if (xml->string_child("Type") == N_("Magick")) { + return shared_ptr (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 + + 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 +#include +#include +#include + +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 () const = 0; + virtual void add_metadata (xmlpp::Node *) const = 0; + virtual void send_binary (boost::shared_ptr) const = 0; +}; + +class RawImageProxy : public ImageProxy +{ +public: + RawImageProxy (boost::shared_ptr); + RawImageProxy (boost::shared_ptr xml, boost::shared_ptr socket); + + boost::shared_ptr image () const; + void add_metadata (xmlpp::Node *) const; + void send_binary (boost::shared_ptr) const; + +private: + boost::shared_ptr _image; +}; + +class MagickImageProxy : public ImageProxy +{ +public: + MagickImageProxy (boost::filesystem::path); + MagickImageProxy (boost::shared_ptr xml, boost::shared_ptr socket); + + boost::shared_ptr image () const; + void add_metadata (xmlpp::Node *) const; + void send_binary (boost::shared_ptr) const; + +private: + Magick::Blob _blob; + mutable boost::shared_ptr _image; +}; + +boost::shared_ptr image_proxy_factory (boost::shared_ptr xml, boost::shared_ptr 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 weak_piece; - boost::shared_ptr image; + boost::shared_ptr 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 a50ab3a37..9f0f380e3 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -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 weak_piece, shared_ptr image, Eyes eyes, bool same, VideoContent::Frame frame, Time extra) +Player::process_video (weak_ptr weak_piece, shared_ptr 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 weak_piece, shared_ptr image _video_container_size, _film->scaler(), eyes, + part, content->colour_conversion() ) ); @@ -421,7 +424,7 @@ Player::setup_pieces () if (fc) { shared_ptr fd (new FFmpegDecoder (_film, fc, _video, _audio)); - fd->Video.connect (bind (&Player::process_video, this, weak_ptr (piece), _1, _2, _3, _4, 0)); + fd->Video.connect (bind (&Player::process_video, this, weak_ptr (piece), _1, _2, _3, _4, _5, 0)); fd->Audio.connect (bind (&Player::process_audio, this, weak_ptr (piece), _1, _2)); fd->Subtitle.connect (bind (&Player::process_subtitle, this, weak_ptr (piece), _1, _2, _3, _4)); @@ -444,7 +447,7 @@ Player::setup_pieces () if (!reusing) { shared_ptr id (new ImageDecoder (_film, ic)); - id->Video.connect (bind (&Player::process_video, this, weak_ptr (piece), _1, _2, _3, _4, 0)); + id->Video.connect (bind (&Player::process_video, this, weak_ptr (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 (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 diff --git a/src/lib/player.h b/src/lib/player.h index eddd8f6a2..bf6260c0a 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -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, boost::shared_ptr, Eyes, bool, VideoContent::Frame, Time); + void process_video (boost::weak_ptr, boost::shared_ptr, Eyes, Part, bool, VideoContent::Frame, Time); void process_audio (boost::weak_ptr, boost::shared_ptr, AudioContent::Frame); void process_subtitle (boost::weak_ptr, boost::shared_ptr, dcpomatic::Rect, Time, Time); void setup_pieces (); diff --git a/src/lib/player_video_frame.cc b/src/lib/player_video_frame.cc index cbe879d10..c96ed3a33 100644 --- a/src/lib/player_video_frame.cc +++ b/src/lib/player_video_frame.cc @@ -20,6 +20,7 @@ #include #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 in, + shared_ptr 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 node, shared_ptrnumber_child ("OutWidth"), node->number_child ("OutHeight")); _scaler = Scaler::from_id (node->string_child ("Scaler")); _eyes = (Eyes) node->number_child ("Eyes"); + _part = (Part) node->number_child ("Part"); _colour_conversion = ColourConversion (node); - shared_ptr image (new Image (PIX_FMT_RGB24, libdcp::Size (node->number_child ("InWidth"), node->number_child ("InHeight")), true)); - image->read_from_socket (socket); - _in = image; + _in = image_proxy_factory (node->node_child ("In"), socket); if (node->optional_number_child ("SubtitleX")) { @@ -84,7 +86,27 @@ PlayerVideoFrame::set_subtitle (shared_ptr image, Position pos shared_ptr PlayerVideoFrame::image () const { - shared_ptr out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false); + shared_ptr 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 out = im->crop_scale_window (total_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false); Position 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 (_in->size().width)); - node->add_child("InHeight")->add_child_text (raw_convert (_in->size().height)); + _in->add_metadata (node->add_child ("In")); node->add_child("InterWidth")->add_child_text (raw_convert (_inter_size.width)); node->add_child("InterHeight")->add_child_text (raw_convert (_inter_size.height)); node->add_child("OutWidth")->add_child_text (raw_convert (_out_size.width)); node->add_child("OutHeight")->add_child_text (raw_convert (_out_size.height)); node->add_child("Scaler")->add_child_text (_scaler->id ()); node->add_child("Eyes")->add_child_text (raw_convert (_eyes)); + node->add_child("Part")->add_child_text (raw_convert (_part)); _colour_conversion.as_xml (node); if (_subtitle_image) { node->add_child ("SubtitleWidth")->add_child_text (raw_convert (_subtitle_image->size().width)); @@ -119,7 +141,7 @@ PlayerVideoFrame::add_metadata (xmlpp::Element* node) const void PlayerVideoFrame::send_binary (shared_ptr socket) const { - _in->write_to_socket (socket); + _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 index ea7e54481..6461134a9 100644 --- a/src/lib/player_video_frame.h +++ b/src/lib/player_video_frame.h @@ -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, Crop, libdcp::Size, libdcp::Size, Scaler const *, Eyes, ColourConversion); + PlayerVideoFrame (boost::shared_ptr, Crop, libdcp::Size, libdcp::Size, Scaler const *, Eyes, Part, ColourConversion); PlayerVideoFrame (boost::shared_ptr, boost::shared_ptr); void set_subtitle (boost::shared_ptr, Position); boost::shared_ptr image () const; - void add_metadata (xmlpp::Element* node) const; + void add_metadata (xmlpp::Node* node) const; void send_binary (boost::shared_ptr socket) const; Eyes eyes () const { @@ -52,12 +53,13 @@ public: } private: - boost::shared_ptr _in; + boost::shared_ptr _in; Crop _crop; libdcp::Size _inter_size; libdcp::Size _out_size; Scaler const * _scaler; Eyes _eyes; + Part _part; ColourConversion _colour_conversion; boost::shared_ptr _subtitle_image; Position _subtitle_position; diff --git a/src/lib/types.h b/src/lib/types.h index 8e2384d80..3fab302fc 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -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. */ 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 f, shared_ptr image, bool same, VideoContent::Frame frame) +VideoDecoder::video (shared_ptr 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, Eyes, bool, VideoContent::Frame)> Video; + boost::signals2::signal, Eyes, Part, bool, VideoContent::Frame)> Video; protected: - void video (boost::shared_ptr, bool, VideoContent::Frame); + void video (boost::shared_ptr, bool, VideoContent::Frame); boost::shared_ptr _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/wscript b/src/lib/wscript index 7e2e10e1f..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 diff --git a/test/client_server_test.cc b/test/client_server_test.cc index 2b0e96775..c8a2b49ef 100644 --- a/test/client_server_test.cc +++ b/test/client_server_test.cc @@ -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 pvf ( new PlayerVideoFrame ( - image, + shared_ptr (new RawImageProxy (image)), Crop (), libdcp::Size (1998, 1080), libdcp::Size (1998, 1080), Scaler::from_id ("bicubic"), EYES_BOTH, + PART_WHOLE, ColourConversion () ) ); -- cgit v1.2.3