2013-12-04 Carl Hetherington <cth@carlh.net>
+ * Only do scale/crop/window/subtitle overlay if a frame is going
+ to be encoded for the DCP.
+
* Several optimisations to video processing, which should
speed up the player a bit.
#include "cross.h"
#include "writer.h"
#include "server_finder.h"
+#include "player.h"
#include "i18n.h"
}
void
-Encoder::process_video (shared_ptr<const Image> image, Eyes eyes, ColourConversion conversion, bool same)
+Encoder::process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, bool same)
{
boost::mutex::scoped_lock lock (_mutex);
TIMING ("adding to queue of %1", _queue.size ());
_queue.push_back (shared_ptr<DCPVideoFrame> (
new DCPVideoFrame (
- image, _video_frames_out, eyes, conversion, _film->video_frame_rate(),
+ image->image(), _video_frames_out, eyes, conversion, _film->video_frame_rate(),
_film->j2k_bandwidth(), _film->log()
)
));
class Writer;
class Job;
class ServerFinder;
+class PlayerImage;
/** @class Encoder
* @brief Encoder to J2K and WAV for DCP.
* @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<const Image> i, Eyes eyes, ColourConversion, bool same);
+ void process_video (boost::shared_ptr<PlayerImage> i, Eyes eyes, ColourConversion, bool same);
/** Call with some audio data */
void process_audio (boost::shared_ptr<const AudioBuffers>);
Time const time = content->position() + relative_time + extra - content->trim_start ();
float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
libdcp::Size const image_size = fit_ratio_within (ratio, _video_container_size);
-
- shared_ptr<Image> work_image = image->crop_scale_window (content->crop(), image_size, _video_container_size, _film->scaler(), PIX_FMT_RGB24, false);
- Position<int> const container_offset (
- (_video_container_size.width - image_size.width) / 2,
- (_video_container_size.height - image_size.width) / 2
+ shared_ptr<PlayerImage> pi (
+ new PlayerImage (
+ image,
+ content->crop(),
+ image_size,
+ _video_container_size,
+ _film->scaler()
+ )
);
-
+
if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
- work_image->alpha_blend (_out_subtitle.image, _out_subtitle.position + container_offset);
- }
+ Position<int> const container_offset (
+ (_video_container_size.width - image_size.width) / 2,
+ (_video_container_size.height - image_size.width) / 2
+ );
+
+ pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
+ }
+
#ifdef DCPOMATIC_DEBUG
_last_video = piece->content;
#endif
- Video (work_image, eyes, content->colour_conversion(), same, time);
+ Video (pi, eyes, content->colour_conversion(), same, time);
_last_emit_was_black = false;
_video_position = piece->video_position = (time + TIME_HZ / _film->video_frame_rate());
Player::set_video_container_size (libdcp::Size s)
{
_video_container_size = s;
- _black_frame.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
- _black_frame->make_black ();
+
+ shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
+ im->make_black ();
+
+ _black_frame.reset (
+ new PlayerImage (
+ im,
+ Crop(),
+ _video_container_size,
+ _video_container_size,
+ Scaler::from_id ("bicubic")
+ )
+ );
}
shared_ptr<Resampler>
return true;
}
+
+PlayerImage::PlayerImage (
+ shared_ptr<const Image> in,
+ Crop crop,
+ libdcp::Size inter_size,
+ libdcp::Size out_size,
+ Scaler const * scaler
+ )
+ : _in (in)
+ , _crop (crop)
+ , _inter_size (inter_size)
+ , _out_size (out_size)
+ , _scaler (scaler)
+{
+
+}
+
+void
+PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
+{
+ _subtitle_image = image;
+ _subtitle_position = pos;
+}
+
+shared_ptr<Image>
+PlayerImage::image ()
+{
+ shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
+
+ Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
+
+ if (_subtitle_image) {
+ out->alpha_blend (_subtitle_image, _subtitle_position);
+ }
+
+ return out;
+}
VideoContent::Frame frame;
Time extra;
};
+
+/** A wrapper for an Image which contains some pending operations; these may
+ * not be necessary if the receiver of the PlayerImage throws it away.
+ */
+class PlayerImage
+{
+public:
+ PlayerImage (boost::shared_ptr<const Image>, Crop, libdcp::Size, libdcp::Size, Scaler const *);
+
+ void set_subtitle (boost::shared_ptr<const Image>, Position<int>);
+
+ boost::shared_ptr<Image> image ();
+
+private:
+ boost::shared_ptr<const Image> _in;
+ Crop _crop;
+ libdcp::Size _inter_size;
+ libdcp::Size _out_size;
+ Scaler const * _scaler;
+ boost::shared_ptr<const Image> _subtitle_image;
+ Position<int> _subtitle_position;
+};
class Player : public boost::enable_shared_from_this<Player>, public boost::noncopyable
{
* 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<void (boost::shared_ptr<const Image>, Eyes, ColourConversion, bool, Time)> Video;
+ boost::signals2::signal<void (boost::shared_ptr<PlayerImage>, Eyes, ColourConversion, bool, Time)> Video;
/** Emitted when some audio data is ready */
boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio;
AudioMerger<Time, AudioContent::Frame> _audio_merger;
libdcp::Size _video_container_size;
- boost::shared_ptr<Image> _black_frame;
+ boost::shared_ptr<PlayerImage> _black_frame;
std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
struct {
using boost::dynamic_pointer_cast;
static void
-video_proxy (weak_ptr<Encoder> encoder, shared_ptr<const Image> image, Eyes eyes, ColourConversion conversion, bool same)
+video_proxy (weak_ptr<Encoder> encoder, shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, bool same)
{
shared_ptr<Encoder> e = encoder.lock ();
if (e) {
static int frame = 0;
void
-process_video (shared_ptr<const Image> image, Eyes eyes, ColourConversion conversion, Time)
+process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, Time)
{
- shared_ptr<DCPVideoFrame> local (new DCPVideoFrame (image, frame, eyes, conversion, film->video_frame_rate(), 250000000, log_));
- shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (image, frame, eyes, conversion, film->video_frame_rate(), 250000000, log_));
+ shared_ptr<DCPVideoFrame> local (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, log_));
+ shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (image->image(), frame, eyes, conversion, film->video_frame_rate(), 250000000, log_));
cout << "Frame " << frame << ": ";
cout.flush ();
}
void
-FilmViewer::process_video (shared_ptr<const Image> image, Eyes eyes, Time t)
+FilmViewer::process_video (shared_ptr<PlayerImage> image, Eyes eyes, Time t)
{
if (eyes == EYES_RIGHT) {
return;
}
- _frame = image;
+ _frame = image->image ();
_got_frame = true;
set_position_text (t);
class FFmpegPlayer;
class Image;
class RGBPlusAlphaImage;
+class PlayerImage;
/** @class FilmViewer
* @brief A wx widget to view a preview of a Film.
void slider_moved ();
void play_clicked ();
void timer ();
- void process_video (boost::shared_ptr<const Image>, Eyes, Time);
+ void process_video (boost::shared_ptr<PlayerImage>, Eyes, Time);
void calculate_sizes ();
void check_play_state ();
void fetch_current_frame_again ();
_player->Video.connect (bind (&PlayerWrapper::process_video, this, _1, _2, _5));
}
- void process_video (shared_ptr<const Image> i, bool, Time t)
+ void process_video (shared_ptr<PlayerImage> i, bool, Time t)
{
Video v;
v.content = _player->_last_video;
- v.image = i;
+ v.image = i->image ();
v.time = t;
_queue.push_front (v);
}