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