Merge branch 'master' of ssh://carlh.dyndns.org/home/carl/git/dcpomatic
[dcpomatic.git] / src / lib / player.cc
1 /*
2     Copyright (C) 2013 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 (fit_ratio_within (_film->container()->ratio (), _film->full_frame ()));
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         float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
277         libdcp::Size const image_size = fit_ratio_within (ratio, _video_container_size);
278
279         shared_ptr<PlayerImage> pi (
280                 new PlayerImage (
281                         image,
282                         content->crop(),
283                         image_size,
284                         _video_container_size,
285                         _film->scaler()
286                         )
287                 );
288         
289         if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
290
291                 Position<int> const container_offset (
292                         (_video_container_size.width - image_size.width) / 2,
293                         (_video_container_size.height - image_size.width) / 2
294                         );
295
296                 pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
297         }
298                 
299                                             
300 #ifdef DCPOMATIC_DEBUG
301         _last_video = piece->content;
302 #endif
303
304         Video (pi, eyes, content->colour_conversion(), same, time);
305
306         _last_emit_was_black = false;
307         _video_position = piece->video_position = (time + TIME_HZ / _film->video_frame_rate());
308
309         if (frc.repeat > 1 && !piece->repeating ()) {
310                 piece->set_repeat (_last_incoming_video, frc.repeat - 1);
311         }
312 }
313
314 void
315 Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
316 {
317         shared_ptr<Piece> piece = weak_piece.lock ();
318         if (!piece) {
319                 return;
320         }
321
322         shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
323         assert (content);
324
325         /* Gain */
326         if (content->audio_gain() != 0) {
327                 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
328                 gain->apply_gain (content->audio_gain ());
329                 audio = gain;
330         }
331
332         /* Resample */
333         if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
334                 shared_ptr<Resampler> r = resampler (content, true);
335                 pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> ro = r->run (audio, frame);
336                 audio = ro.first;
337                 frame = ro.second;
338         }
339         
340         Time const relative_time = _film->audio_frames_to_time (frame);
341
342         if (content->trimmed (relative_time)) {
343                 return;
344         }
345
346         Time time = content->position() + (content->audio_delay() * TIME_HZ / 1000) + relative_time - content->trim_start ();
347         
348         /* Remap channels */
349         shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
350         dcp_mapped->make_silent ();
351
352         AudioMapping map = content->audio_mapping ();
353         for (int i = 0; i < map.content_channels(); ++i) {
354                 for (int j = 0; j < _film->audio_channels(); ++j) {
355                         if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
356                                 dcp_mapped->accumulate_channel (
357                                         audio.get(),
358                                         i,
359                                         static_cast<libdcp::Channel> (j),
360                                         map.get (i, static_cast<libdcp::Channel> (j))
361                                         );
362                         }
363                 }
364         }
365
366         audio = dcp_mapped;
367
368         /* We must cut off anything that comes before the start of all time */
369         if (time < 0) {
370                 int const frames = - time * _film->audio_frame_rate() / TIME_HZ;
371                 if (frames >= audio->frames ()) {
372                         return;
373                 }
374
375                 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
376                 trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
377
378                 audio = trimmed;
379                 time = 0;
380         }
381
382         _audio_merger.push (audio, time);
383         piece->audio_position += _film->audio_frames_to_time (audio->frames ());
384 }
385
386 void
387 Player::flush ()
388 {
389         TimedAudioBuffers<Time> tb = _audio_merger.flush ();
390         if (_audio && tb.audio) {
391                 Audio (tb.audio, tb.time);
392                 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
393         }
394
395         while (_video && _video_position < _audio_position) {
396                 emit_black ();
397         }
398
399         while (_audio && _audio_position < _video_position) {
400                 emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
401         }
402         
403 }
404
405 /** Seek so that the next pass() will yield (approximately) the requested frame.
406  *  Pass accurate = true to try harder to get close to the request.
407  *  @return true on error
408  */
409 void
410 Player::seek (Time t, bool accurate)
411 {
412         if (!_have_valid_pieces) {
413                 setup_pieces ();
414         }
415
416         if (_pieces.empty ()) {
417                 return;
418         }
419
420         for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
421                 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
422                 if (!vc) {
423                         continue;
424                 }
425
426                 /* s is the offset of t from the start position of this content */
427                 Time s = t - vc->position ();
428                 s = max (static_cast<Time> (0), s);
429                 s = min (vc->length_after_trim(), s);
430
431                 /* Hence set the piece positions to the `global' time */
432                 (*i)->video_position = (*i)->audio_position = vc->position() + s;
433
434                 /* And seek the decoder */
435                 dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (
436                         vc->time_to_content_video_frames (s + vc->trim_start ()), accurate
437                         );
438
439                 (*i)->reset_repeat ();
440         }
441
442         _video_position = _audio_position = t;
443
444         /* XXX: don't seek audio because we don't need to... */
445 }
446
447 void
448 Player::setup_pieces ()
449 {
450         list<shared_ptr<Piece> > old_pieces = _pieces;
451
452         _pieces.clear ();
453
454         ContentList content = _playlist->content ();
455         sort (content.begin(), content.end(), ContentSorter ());
456
457         for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
458
459                 if (!(*i)->paths_valid ()) {
460                         continue;
461                 }
462
463                 shared_ptr<Piece> piece (new Piece (*i));
464
465                 /* XXX: into content? */
466
467                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
468                 if (fc) {
469                         shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
470                         
471                         fd->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
472                         fd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
473                         fd->Subtitle.connect (bind (&Player::process_subtitle, this, weak_ptr<Piece> (piece), _1, _2, _3, _4));
474
475                         fd->seek (fc->time_to_content_video_frames (fc->trim_start ()), true);
476                         piece->decoder = fd;
477                 }
478                 
479                 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
480                 if (ic) {
481                         bool reusing = false;
482                         
483                         /* See if we can re-use an old ImageDecoder */
484                         for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
485                                 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
486                                 if (imd && imd->content() == ic) {
487                                         piece = *j;
488                                         reusing = true;
489                                 }
490                         }
491
492                         if (!reusing) {
493                                 shared_ptr<ImageDecoder> id (new ImageDecoder (_film, ic));
494                                 id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
495                                 piece->decoder = id;
496                         }
497                 }
498
499                 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
500                 if (sc) {
501                         shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
502                         sd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
503
504                         piece->decoder = sd;
505                 }
506
507                 _pieces.push_back (piece);
508         }
509
510         _have_valid_pieces = true;
511 }
512
513 void
514 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
515 {
516         shared_ptr<Content> c = w.lock ();
517         if (!c) {
518                 return;
519         }
520
521         if (
522                 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
523                 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
524                 property == VideoContentProperty::VIDEO_FRAME_TYPE 
525                 ) {
526                 
527                 _have_valid_pieces = false;
528                 Changed (frequent);
529
530         } else if (
531                 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
532                 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
533                 property == SubtitleContentProperty::SUBTITLE_SCALE
534                 ) {
535
536                 update_subtitle ();
537                 Changed (frequent);
538
539         } else if (
540                 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
541                 property == VideoContentProperty::VIDEO_FRAME_RATE
542                 ) {
543                 
544                 Changed (frequent);
545
546         } else if (property == ContentProperty::PATH) {
547
548                 _have_valid_pieces = false;
549                 Changed (frequent);
550         }
551 }
552
553 void
554 Player::playlist_changed ()
555 {
556         _have_valid_pieces = false;
557         Changed (false);
558 }
559
560 void
561 Player::set_video_container_size (libdcp::Size s)
562 {
563         _video_container_size = s;
564
565         shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
566         im->make_black ();
567         
568         _black_frame.reset (
569                 new PlayerImage (
570                         im,
571                         Crop(),
572                         _video_container_size,
573                         _video_container_size,
574                         Scaler::from_id ("bicubic")
575                         )
576                 );
577 }
578
579 shared_ptr<Resampler>
580 Player::resampler (shared_ptr<AudioContent> c, bool create)
581 {
582         map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
583         if (i != _resamplers.end ()) {
584                 return i->second;
585         }
586
587         if (!create) {
588                 return shared_ptr<Resampler> ();
589         }
590
591         _film->log()->log (
592                 String::compose (
593                         "Creating new resampler for %1 to %2 with %3 channels", c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()
594                         )
595                 );
596         
597         shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
598         _resamplers[c] = r;
599         return r;
600 }
601
602 void
603 Player::emit_black ()
604 {
605 #ifdef DCPOMATIC_DEBUG
606         _last_video.reset ();
607 #endif
608
609         Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
610         _video_position += _film->video_frames_to_time (1);
611         _last_emit_was_black = true;
612 }
613
614 void
615 Player::emit_silence (OutputAudioFrame most)
616 {
617         if (most == 0) {
618                 return;
619         }
620         
621         OutputAudioFrame N = min (most, _film->audio_frame_rate() / 2);
622         shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), N));
623         silence->make_silent ();
624         Audio (silence, _audio_position);
625         _audio_position += _film->audio_frames_to_time (N);
626 }
627
628 void
629 Player::film_changed (Film::Property p)
630 {
631         /* Here we should notice Film properties that affect our output, and
632            alert listeners that our output now would be different to how it was
633            last time we were run.
634         */
635
636         if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
637                 Changed (false);
638         }
639 }
640
641 void
642 Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
643 {
644         _in_subtitle.piece = weak_piece;
645         _in_subtitle.image = image;
646         _in_subtitle.rect = rect;
647         _in_subtitle.from = from;
648         _in_subtitle.to = to;
649
650         update_subtitle ();
651 }
652
653 void
654 Player::update_subtitle ()
655 {
656         shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
657         if (!piece) {
658                 return;
659         }
660
661         if (!_in_subtitle.image) {
662                 _out_subtitle.image.reset ();
663                 return;
664         }
665
666         shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
667         assert (sc);
668
669         dcpomatic::Rect<double> in_rect = _in_subtitle.rect;
670         libdcp::Size scaled_size;
671
672         in_rect.x += sc->subtitle_x_offset ();
673         in_rect.y += sc->subtitle_y_offset ();
674
675         /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
676         scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
677         scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
678
679         /* Then we need a corrective translation, consisting of two parts:
680          *
681          * 1.  that which is the result of the scaling of the subtitle by _video_container_size; this will be
682          *     rect.x * _video_container_size.width and rect.y * _video_container_size.height.
683          *
684          * 2.  that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
685          *     (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
686          *     (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
687          *
688          * Combining these two translations gives these expressions.
689          */
690         
691         _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
692         _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
693         
694         _out_subtitle.image = _in_subtitle.image->scale (
695                 scaled_size,
696                 Scaler::from_id ("bicubic"),
697                 _in_subtitle.image->pixel_format (),
698                 true
699                 );
700
701         /* XXX: hack */
702         Time from = _in_subtitle.from;
703         Time to = _in_subtitle.to;
704         shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (piece->content);
705         if (vc) {
706                 from = rint (from * vc->video_frame_rate() / _film->video_frame_rate());
707                 to = rint (to * vc->video_frame_rate() / _film->video_frame_rate());
708         }
709         
710         _out_subtitle.from = from + piece->content->position ();
711         _out_subtitle.to = to + piece->content->position ();
712 }
713
714 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
715  *  @return false if this could not be done.
716  */
717 bool
718 Player::repeat_last_video ()
719 {
720         if (!_last_incoming_video.image || !_have_valid_pieces) {
721                 return false;
722         }
723
724         process_video (
725                 _last_incoming_video.weak_piece,
726                 _last_incoming_video.image,
727                 _last_incoming_video.eyes,
728                 _last_incoming_video.same,
729                 _last_incoming_video.frame,
730                 _last_incoming_video.extra
731                 );
732
733         return true;
734 }
735
736 PlayerImage::PlayerImage (
737         shared_ptr<const Image> in,
738         Crop crop,
739         libdcp::Size inter_size,
740         libdcp::Size out_size,
741         Scaler const * scaler
742         )
743         : _in (in)
744         , _crop (crop)
745         , _inter_size (inter_size)
746         , _out_size (out_size)
747         , _scaler (scaler)
748 {
749
750 }
751
752 void
753 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
754 {
755         _subtitle_image = image;
756         _subtitle_position = pos;
757 }
758
759 shared_ptr<Image>
760 PlayerImage::image ()
761 {
762         shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
763
764         Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
765
766         if (_subtitle_image) {
767                 out->alpha_blend (_subtitle_image, _subtitle_position);
768         }
769
770         return out;
771 }