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