Don't overlap simultaneous video content in the timeline. Fix keep-aligned for separ...
[dcpomatic.git] / src / lib / player.cc
1 /*
2     Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <stdint.h>
21 #include "player.h"
22 #include "film.h"
23 #include "ffmpeg_decoder.h"
24 #include "ffmpeg_content.h"
25 #include "image_decoder.h"
26 #include "image_content.h"
27 #include "sndfile_decoder.h"
28 #include "sndfile_content.h"
29 #include "subtitle_content.h"
30 #include "playlist.h"
31 #include "job.h"
32 #include "image.h"
33 #include "ratio.h"
34 #include "resampler.h"
35 #include "log.h"
36 #include "scaler.h"
37
38 using std::list;
39 using std::cout;
40 using std::min;
41 using std::max;
42 using std::vector;
43 using std::pair;
44 using std::map;
45 using boost::shared_ptr;
46 using boost::weak_ptr;
47 using boost::dynamic_pointer_cast;
48
49 class Piece
50 {
51 public:
52         Piece (shared_ptr<Content> c)
53                 : content (c)
54                 , video_position (c->position ())
55                 , audio_position (c->position ())
56                 , repeat_to_do (0)
57                 , repeat_done (0)
58         {}
59         
60         Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
61                 : content (c)
62                 , decoder (d)
63                 , video_position (c->position ())
64                 , audio_position (c->position ())
65                 , repeat_to_do (0)
66                 , repeat_done (0)
67         {}
68
69         /** Set this piece to repeat a video frame a given number of times */
70         void set_repeat (IncomingVideo video, int num)
71         {
72                 repeat_video = video;
73                 repeat_to_do = num;
74                 repeat_done = 0;
75         }
76
77         void reset_repeat ()
78         {
79                 repeat_video.image.reset ();
80                 repeat_to_do = 0;
81                 repeat_done = 0;
82         }
83
84         bool repeating () const
85         {
86                 return repeat_done != repeat_to_do;
87         }
88
89         void repeat (Player* player)
90         {
91                 player->process_video (
92                         repeat_video.weak_piece,
93                         repeat_video.image,
94                         repeat_video.eyes,
95                         repeat_done > 0,
96                         repeat_video.frame,
97                         (repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ())
98                         );
99
100                 ++repeat_done;
101         }
102         
103         shared_ptr<Content> content;
104         shared_ptr<Decoder> decoder;
105         /** Time of the last video we emitted relative to the start of the DCP */
106         Time video_position;
107         /** Time of the last audio we emitted relative to the start of the DCP */
108         Time audio_position;
109
110         IncomingVideo repeat_video;
111         int repeat_to_do;
112         int repeat_done;
113 };
114
115 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
116         : _film (f)
117         , _playlist (p)
118         , _video (true)
119         , _audio (true)
120         , _have_valid_pieces (false)
121         , _video_position (0)
122         , _audio_position (0)
123         , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
124         , _last_emit_was_black (false)
125 {
126         _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
127         _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
128         _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
129         set_video_container_size (_film->frame_size ());
130 }
131
132 void
133 Player::disable_video ()
134 {
135         _video = false;
136 }
137
138 void
139 Player::disable_audio ()
140 {
141         _audio = false;
142 }
143
144 bool
145 Player::pass ()
146 {
147         if (!_have_valid_pieces) {
148                 setup_pieces ();
149         }
150
151         Time earliest_t = TIME_MAX;
152         shared_ptr<Piece> earliest;
153         enum {
154                 VIDEO,
155                 AUDIO
156         } type = VIDEO;
157
158         for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
159                 if ((*i)->decoder->done ()) {
160                         continue;
161                 }
162
163                 shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> ((*i)->decoder);
164                 shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
165
166                 if (_video && vd) {
167                         if ((*i)->video_position < earliest_t) {
168                                 earliest_t = (*i)->video_position;
169                                 earliest = *i;
170                                 type = VIDEO;
171                         }
172                 }
173
174                 if (_audio && ad && ad->has_audio ()) {
175                         if ((*i)->audio_position < earliest_t) {
176                                 earliest_t = (*i)->audio_position;
177                                 earliest = *i;
178                                 type = AUDIO;
179                         }
180                 }
181         }
182
183         if (!earliest) {
184                 flush ();
185                 return true;
186         }
187
188         switch (type) {
189         case VIDEO:
190                 if (earliest_t > _video_position) {
191                         emit_black ();
192                 } else {
193                         if (earliest->repeating ()) {
194                                 earliest->repeat (this);
195                         } else {
196                                 earliest->decoder->pass ();
197                         }
198                 }
199                 break;
200
201         case AUDIO:
202                 if (earliest_t > _audio_position) {
203                         emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
204                 } else {
205                         earliest->decoder->pass ();
206
207                         if (earliest->decoder->done()) {
208                                 shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (earliest->content);
209                                 assert (ac);
210                                 shared_ptr<Resampler> re = resampler (ac, false);
211                                 if (re) {
212                                         shared_ptr<const AudioBuffers> b = re->flush ();
213                                         if (b->frames ()) {
214                                                 process_audio (earliest, b, ac->audio_length ());
215                                         }
216                                 }
217                         }
218                 }
219                 break;
220         }
221
222         if (_audio) {
223                 boost::optional<Time> audio_done_up_to;
224                 for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
225                         if ((*i)->decoder->done ()) {
226                                 continue;
227                         }
228
229                         shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
230                         if (ad && ad->has_audio ()) {
231                                 audio_done_up_to = min (audio_done_up_to.get_value_or (TIME_MAX), (*i)->audio_position);
232                         }
233                 }
234
235                 if (audio_done_up_to) {
236                         TimedAudioBuffers<Time> tb = _audio_merger.pull (audio_done_up_to.get ());
237                         Audio (tb.audio, tb.time);
238                         _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
239                 }
240         }
241                 
242         return false;
243 }
244
245 /** @param extra Amount of extra time to add to the content frame's time (for repeat) */
246 void
247 Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, Eyes eyes, bool same, VideoContent::Frame frame, Time extra)
248 {
249         /* Keep a note of what came in so that we can repeat it if required */
250         _last_incoming_video.weak_piece = weak_piece;
251         _last_incoming_video.image = image;
252         _last_incoming_video.eyes = eyes;
253         _last_incoming_video.same = same;
254         _last_incoming_video.frame = frame;
255         _last_incoming_video.extra = extra;
256         
257         shared_ptr<Piece> piece = weak_piece.lock ();
258         if (!piece) {
259                 return;
260         }
261
262         shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
263         assert (content);
264
265         FrameRateConversion frc (content->video_frame_rate(), _film->video_frame_rate());
266         if (frc.skip && (frame % 2) == 1) {
267                 return;
268         }
269
270         Time const relative_time = (frame * frc.factor() * TIME_HZ / _film->video_frame_rate());
271         if (content->trimmed (relative_time)) {
272                 return;
273         }
274
275         Time const time = content->position() + relative_time + extra - content->trim_start ();
276         libdcp::Size const image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
277
278         shared_ptr<PlayerImage> pi (
279                 new PlayerImage (
280                         image,
281                         content->crop(),
282                         image_size,
283                         _video_container_size,
284                         _film->scaler()
285                         )
286                 );
287         
288         if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
289
290                 Position<int> const container_offset (
291                         (_video_container_size.width - image_size.width) / 2,
292                         (_video_container_size.height - image_size.width) / 2
293                         );
294
295                 pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
296         }
297                 
298                                             
299 #ifdef DCPOMATIC_DEBUG
300         _last_video = piece->content;
301 #endif
302
303         Video (pi, eyes, content->colour_conversion(), same, time);
304
305         _last_emit_was_black = false;
306         _video_position = piece->video_position = (time + TIME_HZ / _film->video_frame_rate());
307
308         if (frc.repeat > 1 && !piece->repeating ()) {
309                 piece->set_repeat (_last_incoming_video, frc.repeat - 1);
310         }
311 }
312
313 void
314 Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
315 {
316         shared_ptr<Piece> piece = weak_piece.lock ();
317         if (!piece) {
318                 return;
319         }
320
321         shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
322         assert (content);
323
324         /* Gain */
325         if (content->audio_gain() != 0) {
326                 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
327                 gain->apply_gain (content->audio_gain ());
328                 audio = gain;
329         }
330
331         /* Resample */
332         if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
333                 shared_ptr<Resampler> r = resampler (content, true);
334                 pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> ro = r->run (audio, frame);
335                 audio = ro.first;
336                 frame = ro.second;
337         }
338         
339         Time const relative_time = _film->audio_frames_to_time (frame);
340
341         if (content->trimmed (relative_time)) {
342                 return;
343         }
344
345         Time time = content->position() + (content->audio_delay() * TIME_HZ / 1000) + relative_time - content->trim_start ();
346         
347         /* Remap channels */
348         shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
349         dcp_mapped->make_silent ();
350
351         AudioMapping map = content->audio_mapping ();
352         for (int i = 0; i < map.content_channels(); ++i) {
353                 for (int j = 0; j < _film->audio_channels(); ++j) {
354                         if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
355                                 dcp_mapped->accumulate_channel (
356                                         audio.get(),
357                                         i,
358                                         static_cast<libdcp::Channel> (j),
359                                         map.get (i, static_cast<libdcp::Channel> (j))
360                                         );
361                         }
362                 }
363         }
364
365         audio = dcp_mapped;
366
367         /* We must cut off anything that comes before the start of all time */
368         if (time < 0) {
369                 int const frames = - time * _film->audio_frame_rate() / TIME_HZ;
370                 if (frames >= audio->frames ()) {
371                         return;
372                 }
373
374                 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
375                 trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
376
377                 audio = trimmed;
378                 time = 0;
379         }
380
381         _audio_merger.push (audio, time);
382         piece->audio_position += _film->audio_frames_to_time (audio->frames ());
383 }
384
385 void
386 Player::flush ()
387 {
388         TimedAudioBuffers<Time> tb = _audio_merger.flush ();
389         if (_audio && tb.audio) {
390                 Audio (tb.audio, tb.time);
391                 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
392         }
393
394         while (_video && _video_position < _audio_position) {
395                 emit_black ();
396         }
397
398         while (_audio && _audio_position < _video_position) {
399                 emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
400         }
401         
402 }
403
404 /** Seek so that the next pass() will yield (approximately) the requested frame.
405  *  Pass accurate = true to try harder to get close to the request.
406  *  @return true on error
407  */
408 void
409 Player::seek (Time t, bool accurate)
410 {
411         if (!_have_valid_pieces) {
412                 setup_pieces ();
413         }
414
415         if (_pieces.empty ()) {
416                 return;
417         }
418
419         for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
420                 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
421                 if (!vc) {
422                         continue;
423                 }
424
425                 /* s is the offset of t from the start position of this content */
426                 Time s = t - vc->position ();
427                 s = max (static_cast<Time> (0), s);
428                 s = min (vc->length_after_trim(), s);
429
430                 /* Hence set the piece positions to the `global' time */
431                 (*i)->video_position = (*i)->audio_position = vc->position() + s;
432
433                 /* And seek the decoder */
434                 dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (
435                         vc->time_to_content_video_frames (s + vc->trim_start ()), accurate
436                         );
437
438                 (*i)->reset_repeat ();
439         }
440
441         _video_position = _audio_position = t;
442
443         /* XXX: don't seek audio because we don't need to... */
444 }
445
446 void
447 Player::setup_pieces ()
448 {
449         list<shared_ptr<Piece> > old_pieces = _pieces;
450
451         _pieces.clear ();
452
453         ContentList content = _playlist->content ();
454         sort (content.begin(), content.end(), ContentSorter ());
455
456         for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
457
458                 if (!(*i)->paths_valid ()) {
459                         continue;
460                 }
461
462                 shared_ptr<Piece> piece (new Piece (*i));
463
464                 /* XXX: into content? */
465
466                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
467                 if (fc) {
468                         shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
469                         
470                         fd->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
471                         fd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
472                         fd->Subtitle.connect (bind (&Player::process_subtitle, this, weak_ptr<Piece> (piece), _1, _2, _3, _4));
473
474                         fd->seek (fc->time_to_content_video_frames (fc->trim_start ()), true);
475                         piece->decoder = fd;
476                 }
477                 
478                 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
479                 if (ic) {
480                         bool reusing = false;
481                         
482                         /* See if we can re-use an old ImageDecoder */
483                         for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
484                                 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
485                                 if (imd && imd->content() == ic) {
486                                         piece = *j;
487                                         reusing = true;
488                                 }
489                         }
490
491                         if (!reusing) {
492                                 shared_ptr<ImageDecoder> id (new ImageDecoder (_film, ic));
493                                 id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
494                                 piece->decoder = id;
495                         }
496                 }
497
498                 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
499                 if (sc) {
500                         shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
501                         sd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
502
503                         piece->decoder = sd;
504                 }
505
506                 _pieces.push_back (piece);
507         }
508
509         _have_valid_pieces = true;
510 }
511
512 void
513 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
514 {
515         shared_ptr<Content> c = w.lock ();
516         if (!c) {
517                 return;
518         }
519
520         if (
521                 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
522                 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
523                 property == VideoContentProperty::VIDEO_FRAME_TYPE 
524                 ) {
525                 
526                 _have_valid_pieces = false;
527                 Changed (frequent);
528
529         } else if (
530                 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
531                 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
532                 property == SubtitleContentProperty::SUBTITLE_SCALE
533                 ) {
534
535                 update_subtitle ();
536                 Changed (frequent);
537
538         } else if (
539                 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_SCALE ||
540                 property == VideoContentProperty::VIDEO_FRAME_RATE
541                 ) {
542                 
543                 Changed (frequent);
544
545         } else if (property == ContentProperty::PATH) {
546
547                 _have_valid_pieces = false;
548                 Changed (frequent);
549         }
550 }
551
552 void
553 Player::playlist_changed ()
554 {
555         _have_valid_pieces = false;
556         Changed (false);
557 }
558
559 void
560 Player::set_video_container_size (libdcp::Size s)
561 {
562         _video_container_size = s;
563
564         shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
565         im->make_black ();
566         
567         _black_frame.reset (
568                 new PlayerImage (
569                         im,
570                         Crop(),
571                         _video_container_size,
572                         _video_container_size,
573                         Scaler::from_id ("bicubic")
574                         )
575                 );
576 }
577
578 shared_ptr<Resampler>
579 Player::resampler (shared_ptr<AudioContent> c, bool create)
580 {
581         map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
582         if (i != _resamplers.end ()) {
583                 return i->second;
584         }
585
586         if (!create) {
587                 return shared_ptr<Resampler> ();
588         }
589
590         _film->log()->log (
591                 String::compose (
592                         "Creating new resampler for %1 to %2 with %3 channels", c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()
593                         )
594                 );
595         
596         shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
597         _resamplers[c] = r;
598         return r;
599 }
600
601 void
602 Player::emit_black ()
603 {
604 #ifdef DCPOMATIC_DEBUG
605         _last_video.reset ();
606 #endif
607
608         Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
609         _video_position += _film->video_frames_to_time (1);
610         _last_emit_was_black = true;
611 }
612
613 void
614 Player::emit_silence (OutputAudioFrame most)
615 {
616         if (most == 0) {
617                 return;
618         }
619         
620         OutputAudioFrame N = min (most, _film->audio_frame_rate() / 2);
621         shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), N));
622         silence->make_silent ();
623         Audio (silence, _audio_position);
624         _audio_position += _film->audio_frames_to_time (N);
625 }
626
627 void
628 Player::film_changed (Film::Property p)
629 {
630         /* Here we should notice Film properties that affect our output, and
631            alert listeners that our output now would be different to how it was
632            last time we were run.
633         */
634
635         if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
636                 Changed (false);
637         }
638 }
639
640 void
641 Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
642 {
643         _in_subtitle.piece = weak_piece;
644         _in_subtitle.image = image;
645         _in_subtitle.rect = rect;
646         _in_subtitle.from = from;
647         _in_subtitle.to = to;
648
649         update_subtitle ();
650 }
651
652 void
653 Player::update_subtitle ()
654 {
655         shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
656         if (!piece) {
657                 return;
658         }
659
660         if (!_in_subtitle.image) {
661                 _out_subtitle.image.reset ();
662                 return;
663         }
664
665         shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
666         assert (sc);
667
668         dcpomatic::Rect<double> in_rect = _in_subtitle.rect;
669         libdcp::Size scaled_size;
670
671         in_rect.x += sc->subtitle_x_offset ();
672         in_rect.y += sc->subtitle_y_offset ();
673
674         /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
675         scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
676         scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
677
678         /* Then we need a corrective translation, consisting of two parts:
679          *
680          * 1.  that which is the result of the scaling of the subtitle by _video_container_size; this will be
681          *     rect.x * _video_container_size.width and rect.y * _video_container_size.height.
682          *
683          * 2.  that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
684          *     (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
685          *     (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
686          *
687          * Combining these two translations gives these expressions.
688          */
689         
690         _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
691         _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
692         
693         _out_subtitle.image = _in_subtitle.image->scale (
694                 scaled_size,
695                 Scaler::from_id ("bicubic"),
696                 _in_subtitle.image->pixel_format (),
697                 true
698                 );
699
700         /* XXX: hack */
701         Time from = _in_subtitle.from;
702         Time to = _in_subtitle.to;
703         shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (piece->content);
704         if (vc) {
705                 from = rint (from * vc->video_frame_rate() / _film->video_frame_rate());
706                 to = rint (to * vc->video_frame_rate() / _film->video_frame_rate());
707         }
708         
709         _out_subtitle.from = from + piece->content->position ();
710         _out_subtitle.to = to + piece->content->position ();
711 }
712
713 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
714  *  @return false if this could not be done.
715  */
716 bool
717 Player::repeat_last_video ()
718 {
719         if (!_last_incoming_video.image || !_have_valid_pieces) {
720                 return false;
721         }
722
723         process_video (
724                 _last_incoming_video.weak_piece,
725                 _last_incoming_video.image,
726                 _last_incoming_video.eyes,
727                 _last_incoming_video.same,
728                 _last_incoming_video.frame,
729                 _last_incoming_video.extra
730                 );
731
732         return true;
733 }
734
735 PlayerImage::PlayerImage (
736         shared_ptr<const Image> in,
737         Crop crop,
738         libdcp::Size inter_size,
739         libdcp::Size out_size,
740         Scaler const * scaler
741         )
742         : _in (in)
743         , _crop (crop)
744         , _inter_size (inter_size)
745         , _out_size (out_size)
746         , _scaler (scaler)
747 {
748
749 }
750
751 void
752 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
753 {
754         _subtitle_image = image;
755         _subtitle_position = pos;
756 }
757
758 shared_ptr<Image>
759 PlayerImage::image ()
760 {
761         shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
762
763         Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
764
765         if (_subtitle_image) {
766                 out->alpha_blend (_subtitle_image, _subtitle_position);
767         }
768
769         return out;
770 }