+ _have_valid_pieces = false;
+ Changed (false);
+}
+
+void
+Player::set_video_container_size (libdcp::Size s)
+{
+ _video_container_size = s;
+
+ shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
+ im->make_black ();
+
+ _black_frame.reset (
+ new PlayerVideoFrame (
+ shared_ptr<ImageProxy> (new RawImageProxy (im, _film->log ())),
+ Crop(),
+ _video_container_size,
+ _video_container_size,
+ Scaler::from_id ("bicubic"),
+ EYES_BOTH,
+ PART_WHOLE,
+ ColourConversion ()
+ )
+ );
+}
+
+shared_ptr<Resampler>
+Player::resampler (shared_ptr<AudioContent> c, bool create)
+{
+ map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
+ if (i != _resamplers.end ()) {
+ return i->second;
+ }
+
+ if (!create) {
+ return shared_ptr<Resampler> ();
+ }
+
+ LOG_GENERAL (
+ "Creating new resampler for %1 to %2 with %3 channels", c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()
+ );
+
+ shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
+ _resamplers[c] = r;
+ return r;
+}
+
+void
+Player::emit_black ()
+{
+#ifdef DCPOMATIC_DEBUG
+ _last_video.reset ();
+#endif
+
+ Video (_black_frame, _last_emit_was_black, _video_position);
+ _video_position += _film->video_frames_to_time (1);
+ _last_emit_was_black = true;
+}
+
+void
+Player::emit_silence (OutputAudioFrame most)
+{
+ if (most == 0) {
+ return;
+ }
+
+ OutputAudioFrame N = min (most, _film->audio_frame_rate() / 2);
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), N));
+ silence->make_silent ();
+ Audio (silence, _audio_position);
+ _audio_position += _film->audio_frames_to_time (N);
+}
+
+void
+Player::film_changed (Film::Property p)
+{
+ /* Here we should notice Film properties that affect our output, and
+ alert listeners that our output now would be different to how it was
+ last time we were run.
+ */
+
+ if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
+ Changed (false);
+ }
+}
+
+void
+Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
+{
+ if (!image) {
+ /* A null image means that we should stop any current subtitles at `from' */
+ for (list<Subtitle>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+ i->set_stop (from);
+ }
+ } else {
+ _subtitles.push_back (Subtitle (_film, _video_container_size, weak_piece, image, rect, from, to));
+ }
+}
+
+/** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
+ * @return false if this could not be done.
+ */
+bool
+Player::repeat_last_video ()
+{
+ if (!_last_incoming_video.image || !_have_valid_pieces) {
+ return false;
+ }
+
+ process_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
+ );
+
+ return true;