Merge master.
[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 <algorithm>
22 #include "player.h"
23 #include "film.h"
24 #include "ffmpeg_decoder.h"
25 #include "ffmpeg_content.h"
26 #include "image_decoder.h"
27 #include "image_content.h"
28 #include "sndfile_decoder.h"
29 #include "sndfile_content.h"
30 #include "subtitle_content.h"
31 #include "subrip_decoder.h"
32 #include "subrip_content.h"
33 #include "playlist.h"
34 #include "job.h"
35 #include "image.h"
36 #include "ratio.h"
37 #include "log.h"
38 #include "scaler.h"
39 #include "render_subtitles.h"
40
41 using std::list;
42 using std::cout;
43 using std::min;
44 using std::max;
45 using std::vector;
46 using std::pair;
47 using std::map;
48 using boost::shared_ptr;
49 using boost::weak_ptr;
50 using boost::dynamic_pointer_cast;
51 using boost::optional;
52
53 class Piece
54 {
55 public:
56         Piece (shared_ptr<Content> c, shared_ptr<Decoder> d, FrameRateChange f)
57                 : content (c)
58                 , decoder (d)
59                 , frc (f)
60         {}
61
62         shared_ptr<Content> content;
63         shared_ptr<Decoder> decoder;
64         FrameRateChange frc;
65 };
66
67 Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
68         : _film (f)
69         , _playlist (p)
70         , _video (true)
71         , _audio (true)
72         , _have_valid_pieces (false)
73         , _video_position (0)
74         , _audio_position (0)
75         , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
76         , _last_emit_was_black (false)
77         , _just_did_inaccurate_seek (false)
78         , _approximate_size (false)
79 {
80         _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
81         _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
82         _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
83         set_video_container_size (fit_ratio_within (_film->container()->ratio (), _film->full_frame ()));
84 }
85
86 void
87 Player::disable_video ()
88 {
89         _video = false;
90 }
91
92 void
93 Player::disable_audio ()
94 {
95         _audio = false;
96 }
97
98 bool
99 Player::pass ()
100 {
101         if (!_have_valid_pieces) {
102                 setup_pieces ();
103         }
104
105         /* Interrogate all our pieces to find the one with the earliest decoded data */
106
107         shared_ptr<Piece> earliest_piece;
108         shared_ptr<Decoded> earliest_decoded;
109         DCPTime earliest_time = TIME_MAX;
110         DCPTime earliest_audio = TIME_MAX;
111
112         for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
113
114                 DCPTime const offset = (*i)->content->position() - (*i)->content->trim_start();
115                 
116                 bool done = false;
117                 shared_ptr<Decoded> dec;
118                 while (!done) {
119                         dec = (*i)->decoder->peek ();
120                         if (!dec) {
121                                 /* Decoder has nothing else to give us */
122                                 break;
123                         }
124
125
126                         dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset);
127                         DCPTime const t = dec->dcp_time - offset;
128                         cout << "Peeked " << (*i)->content->paths()[0] << " for " << t << " cf " << ((*i)->content->full_length() - (*i)->content->trim_end ()) << "\n";
129                         if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) {
130                                 /* In the end-trimmed part; decoder has nothing else to give us */
131                                 dec.reset ();
132                                 done = true;
133                         } else if (t >= (*i)->content->trim_start ()) {
134                                 /* Within the un-trimmed part; everything's ok */
135                                 done = true;
136                         } else {
137                                 /* Within the start-trimmed part; get something else */
138                                 (*i)->decoder->consume ();
139                         }
140                 }
141
142                 if (!dec) {
143                         continue;
144                 }
145
146                 if (dec->dcp_time < earliest_time) {
147                         earliest_piece = *i;
148                         earliest_decoded = dec;
149                         earliest_time = dec->dcp_time;
150                 }
151
152                 if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
153                         earliest_audio = dec->dcp_time;
154                 }
155         }
156                 
157         if (!earliest_piece) {
158                 flush ();
159                 return true;
160         }
161
162         if (earliest_audio != TIME_MAX) {
163                 TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (max (int64_t (0), earliest_audio));
164                 Audio (tb.audio, tb.time);
165                 /* This assumes that the audio_frames_to_time conversion is exact
166                    so that there are no accumulated errors caused by rounding.
167                 */
168                 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
169         }
170
171         /* Emit the earliest thing */
172
173         shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
174         shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
175         shared_ptr<DecodedImageSubtitle> dis = dynamic_pointer_cast<DecodedImageSubtitle> (earliest_decoded);
176         shared_ptr<DecodedTextSubtitle> dts = dynamic_pointer_cast<DecodedTextSubtitle> (earliest_decoded);
177
178         if (dv) {
179                 cout << "Video @ " << dv->dcp_time << " " << (double(dv->dcp_time) / TIME_HZ) << ".\n";
180         } else if (da) {
181                 cout << "Audio.\n";
182         } else if (dis) {
183                 cout << "Image sub.\n";
184         } else if (dts) {
185                 cout << "Text sub.\n";
186         }
187         
188         /* Will be set to false if we shouldn't consume the peeked DecodedThing */
189         bool consume = true;
190
191         if (dv && _video) {
192
193                 if (_just_did_inaccurate_seek) {
194
195                         /* Just emit; no subtlety */
196                         emit_video (earliest_piece, dv);
197                         step_video_position (dv);
198                         
199                 } else if (dv->dcp_time > _video_position) {
200
201                         /* Too far ahead */
202
203                         list<shared_ptr<Piece> >::iterator i = _pieces.begin();
204                         while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) {
205                                 ++i;
206                         }
207
208                         if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
209                                 /* We're outside all video content */
210                                 emit_black ();
211                                 _statistics.video.black++;
212                         } else {
213                                 /* We're inside some video; repeat the frame */
214                                 _last_incoming_video.video->dcp_time = _video_position;
215                                 emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
216                                 step_video_position (_last_incoming_video.video);
217                                 _statistics.video.repeat++;
218                         }
219
220                         consume = false;
221
222                 } else if (dv->dcp_time == _video_position) {
223                         /* We're ok */
224                         emit_video (earliest_piece, dv);
225                         step_video_position (dv);
226                         _statistics.video.good++;
227                 } else {
228                         /* Too far behind: skip */
229                         _statistics.video.skip++;
230                 }
231
232                 _just_did_inaccurate_seek = false;
233
234         } else if (da && _audio) {
235
236                 if (da->dcp_time > _audio_position) {
237                         /* Too far ahead */
238                         emit_silence (da->dcp_time - _audio_position);
239                         consume = false;
240                         _statistics.audio.silence += (da->dcp_time - _audio_position);
241                 } else if (da->dcp_time == _audio_position) {
242                         /* We're ok */
243                         emit_audio (earliest_piece, da);
244                         _statistics.audio.good += da->data->frames();
245                 } else {
246                         /* Too far behind: skip */
247                         _statistics.audio.skip += da->data->frames();
248                 }
249                 
250         } else if (dis && _video) {
251                 _image_subtitle.piece = earliest_piece;
252                 _image_subtitle.subtitle = dis;
253                 update_subtitle_from_image ();
254         } else if (dts && _video) {
255                 _text_subtitle.piece = earliest_piece;
256                 _text_subtitle.subtitle = dts;
257                 update_subtitle_from_text ();
258         }
259
260         if (consume) {
261                 earliest_piece->decoder->consume ();
262         }                       
263         
264         return false;
265 }
266
267 void
268 Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
269 {
270         /* Keep a note of what came in so that we can repeat it if required */
271         _last_incoming_video.weak_piece = weak_piece;
272         _last_incoming_video.video = video;
273         
274         shared_ptr<Piece> piece = weak_piece.lock ();
275         if (!piece) {
276                 return;
277         }
278
279         shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
280         assert (content);
281
282         FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
283
284         float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
285         dcp::Size image_size = fit_ratio_within (ratio, _video_container_size);
286         if (_approximate_size) {
287                 image_size.width &= ~3;
288                 image_size.height &= ~3;
289         }
290
291         shared_ptr<PlayerImage> pi (
292                 new PlayerImage (
293                         video->image,
294                         content->crop(),
295                         image_size,
296                         _video_container_size,
297                         _film->scaler()
298                         )
299                 );
300         
301         if (
302                 _film->with_subtitles () &&
303                 _out_subtitle.image &&
304                 video->dcp_time >= _out_subtitle.from && video->dcp_time <= _out_subtitle.to
305                 ) {
306
307                 Position<int> const container_offset (
308                         (_video_container_size.width - image_size.width) / 2,
309                         (_video_container_size.height - image_size.height) / 2
310                         );
311
312                 pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
313         }
314                 
315                                             
316 #ifdef DCPOMATIC_DEBUG
317         _last_video = piece->content;
318 #endif
319
320         Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
321         
322         _last_emit_was_black = false;
323 }
324
325 void
326 Player::step_video_position (shared_ptr<DecodedVideo> video)
327 {
328         /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
329         if (video->eyes != EYES_LEFT) {
330                 /* This assumes that the video_frames_to_time conversion is exact
331                    so that there are no accumulated errors caused by rounding.
332                 */
333                 _video_position += _film->video_frames_to_time (1);
334         }
335 }
336
337 void
338 Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
339 {
340         shared_ptr<Piece> piece = weak_piece.lock ();
341         if (!piece) {
342                 return;
343         }
344
345         shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
346         assert (content);
347
348         /* Gain */
349         if (content->audio_gain() != 0) {
350                 shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
351                 gain->apply_gain (content->audio_gain ());
352                 audio->data = gain;
353         }
354
355         /* Remap channels */
356         shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
357         dcp_mapped->make_silent ();
358         AudioMapping map = content->audio_mapping ();
359         for (int i = 0; i < map.content_channels(); ++i) {
360                 for (int j = 0; j < _film->audio_channels(); ++j) {
361                         if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
362                                 dcp_mapped->accumulate_channel (
363                                         audio->data.get(),
364                                         i,
365                                         static_cast<dcp::Channel> (j),
366                                         map.get (i, static_cast<dcp::Channel> (j))
367                                         );
368                         }
369                 }
370         }
371
372         audio->data = dcp_mapped;
373
374         /* Delay */
375         audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
376         if (audio->dcp_time < 0) {
377                 int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
378                 if (frames >= audio->data->frames ()) {
379                         return;
380                 }
381
382                 shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
383                 trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
384
385                 audio->data = trimmed;
386                 audio->dcp_time = 0;
387         }
388
389         _audio_merger.push (audio->data, audio->dcp_time);
390 }
391
392 void
393 Player::flush ()
394 {
395         TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
396         if (_audio && tb.audio) {
397                 Audio (tb.audio, tb.time);
398                 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
399         }
400
401         while (_video && _video_position < _audio_position) {
402                 emit_black ();
403         }
404
405         while (_audio && _audio_position < _video_position) {
406                 emit_silence (_video_position - _audio_position);
407         }
408 }
409
410 /** Seek so that the next pass() will yield (approximately) the requested frame.
411  *  Pass accurate = true to try harder to get close to the request.
412  *  @return true on error
413  */
414 void
415 Player::seek (DCPTime t, bool accurate)
416 {
417         if (!_have_valid_pieces) {
418                 setup_pieces ();
419         }
420
421         if (_pieces.empty ()) {
422                 return;
423         }
424
425         for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
426                 /* s is the offset of t from the start position of this content */
427                 DCPTime s = t - (*i)->content->position ();
428                 s = max (static_cast<DCPTime> (0), s);
429                 s = min ((*i)->content->length_after_trim(), s);
430
431                 /* Convert this to the content time */
432                 ContentTime ct = (s + (*i)->content->trim_start()) * (*i)->frc.speed_up;
433
434                 /* And seek the decoder */
435                 cout << "seek " << (*i)->content->paths()[0] << " to " << ct << "\n";
436                 (*i)->decoder->seek (ct, accurate);
437         }
438
439         _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
440         _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
441
442         _audio_merger.clear (_audio_position);
443
444         if (!accurate) {
445                 /* We just did an inaccurate seek, so it's likely that the next thing seen
446                    out of pass() will be a fair distance from _{video,audio}_position.  Setting
447                    this flag stops pass() from trying to fix that: we assume that if it
448                    was an inaccurate seek then the caller does not care too much about
449                    inserting black/silence to keep the time tidy.
450                 */
451                 _just_did_inaccurate_seek = true;
452         }
453 }
454
455 void
456 Player::setup_pieces ()
457 {
458         list<shared_ptr<Piece> > old_pieces = _pieces;
459         _pieces.clear ();
460
461         ContentList content = _playlist->content ();
462
463         for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
464
465                 if (!(*i)->paths_valid ()) {
466                         continue;
467                 }
468                 
469                 shared_ptr<Decoder> decoder;
470                 optional<FrameRateChange> frc;
471
472                 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
473                 DCPTime best_overlap_t = 0;
474                 shared_ptr<VideoContent> best_overlap;
475                 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
476                         shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
477                         if (!vc) {
478                                 continue;
479                         }
480                         
481                         DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
482                         if (overlap > best_overlap_t) {
483                                 best_overlap = vc;
484                                 best_overlap_t = overlap;
485                         }
486                 }
487
488                 optional<FrameRateChange> best_overlap_frc;
489                 if (best_overlap) {
490                         best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
491                 } else {
492                         /* No video overlap; e.g. if the DCP is just audio */
493                         best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
494                 }
495
496                 /* FFmpeg */
497                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
498                 if (fc) {
499                         decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
500                         frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
501                 }
502
503                 /* ImageContent */
504                 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
505                 if (ic) {
506                         /* See if we can re-use an old ImageDecoder */
507                         for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
508                                 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
509                                 if (imd && imd->content() == ic) {
510                                         decoder = imd;
511                                 }
512                         }
513
514                         if (!decoder) {
515                                 decoder.reset (new ImageDecoder (_film, ic));
516                         }
517
518                         frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
519                 }
520
521                 /* SndfileContent */
522                 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
523                 if (sc) {
524                         decoder.reset (new SndfileDecoder (_film, sc));
525                         frc = best_overlap_frc;
526                 }
527
528                 /* SubRipContent */
529                 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
530                 if (rc) {
531                         decoder.reset (new SubRipDecoder (_film, rc));
532                         frc = best_overlap_frc;
533                 }
534
535                 ContentTime st = (*i)->trim_start() * frc->speed_up;
536                 decoder->seek (st, true);
537                 
538                 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
539         }
540
541         _have_valid_pieces = true;
542
543         /* The Piece for the _last_incoming_video will no longer be valid */
544         _last_incoming_video.video.reset ();
545
546         _video_position = _audio_position = 0;
547 }
548
549 void
550 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
551 {
552         shared_ptr<Content> c = w.lock ();
553         if (!c) {
554                 return;
555         }
556
557         if (
558                 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
559                 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
560                 property == VideoContentProperty::VIDEO_FRAME_TYPE 
561                 ) {
562                 
563                 _have_valid_pieces = false;
564                 Changed (frequent);
565
566         } else if (
567                 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
568                 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
569                 property == SubtitleContentProperty::SUBTITLE_SCALE
570                 ) {
571
572                 update_subtitle_from_image ();
573                 update_subtitle_from_text ();
574                 Changed (frequent);
575
576         } else if (
577                 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
578                 property == VideoContentProperty::VIDEO_FRAME_RATE
579                 ) {
580                 
581                 Changed (frequent);
582
583         } else if (property == ContentProperty::PATH) {
584
585                 _have_valid_pieces = false;
586                 Changed (frequent);
587         }
588 }
589
590 void
591 Player::playlist_changed ()
592 {
593         _have_valid_pieces = false;
594         Changed (false);
595 }
596
597 void
598 Player::set_video_container_size (dcp::Size s)
599 {
600         _video_container_size = s;
601
602         shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
603         im->make_black ();
604         
605         _black_frame.reset (
606                 new PlayerImage (
607                         im,
608                         Crop(),
609                         _video_container_size,
610                         _video_container_size,
611                         Scaler::from_id ("bicubic")
612                         )
613                 );
614 }
615
616 void
617 Player::emit_black ()
618 {
619 #ifdef DCPOMATIC_DEBUG
620         _last_video.reset ();
621 #endif
622
623         Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
624         _video_position += _film->video_frames_to_time (1);
625         _last_emit_was_black = true;
626 }
627
628 void
629 Player::emit_silence (DCPTime most)
630 {
631         if (most == 0) {
632                 return;
633         }
634         
635         DCPTime t = min (most, TIME_HZ / 2);
636         shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
637         silence->make_silent ();
638         Audio (silence, _audio_position);
639         
640         _audio_position += t;
641 }
642
643 void
644 Player::film_changed (Film::Property p)
645 {
646         /* Here we should notice Film properties that affect our output, and
647            alert listeners that our output now would be different to how it was
648            last time we were run.
649         */
650
651         if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
652                 Changed (false);
653         }
654 }
655
656 void
657 Player::update_subtitle_from_image ()
658 {
659         shared_ptr<Piece> piece = _image_subtitle.piece.lock ();
660         if (!piece) {
661                 return;
662         }
663
664         if (!_image_subtitle.subtitle->image) {
665                 _out_subtitle.image.reset ();
666                 return;
667         }
668
669         shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
670         assert (sc);
671
672         dcpomatic::Rect<double> in_rect = _image_subtitle.subtitle->rect;
673         dcp::Size scaled_size;
674
675         in_rect.x += sc->subtitle_x_offset ();
676         in_rect.y += sc->subtitle_y_offset ();
677
678         /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
679         scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
680         scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
681
682         /* Then we need a corrective translation, consisting of two parts:
683          *
684          * 1.  that which is the result of the scaling of the subtitle by _video_container_size; this will be
685          *     rect.x * _video_container_size.width and rect.y * _video_container_size.height.
686          *
687          * 2.  that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
688          *     (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
689          *     (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
690          *
691          * Combining these two translations gives these expressions.
692          */
693         
694         _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
695         _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
696         
697         _out_subtitle.image = _image_subtitle.subtitle->image->scale (
698                 scaled_size,
699                 Scaler::from_id ("bicubic"),
700                 _image_subtitle.subtitle->image->pixel_format (),
701                 true
702                 );
703         
704         _out_subtitle.from = _image_subtitle.subtitle->dcp_time + piece->content->position ();
705         _out_subtitle.to = _image_subtitle.subtitle->dcp_time_to + piece->content->position ();
706 }
707
708 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
709  *  @return false if this could not be done.
710  */
711 bool
712 Player::repeat_last_video ()
713 {
714         if (!_last_incoming_video.video || !_have_valid_pieces) {
715                 return false;
716         }
717
718         emit_video (
719                 _last_incoming_video.weak_piece,
720                 _last_incoming_video.video
721                 );
722
723         return true;
724 }
725
726 void
727 Player::update_subtitle_from_text ()
728 {
729         if (_text_subtitle.subtitle->subs.empty ()) {
730                 _out_subtitle.image.reset ();
731                 return;
732         }
733
734         render_subtitles (_text_subtitle.subtitle->subs, _video_container_size, _out_subtitle.image, _out_subtitle.position);
735 }
736
737 void
738 Player::set_approximate_size ()
739 {
740         _approximate_size = true;
741 }
742                               
743 PlayerImage::PlayerImage (
744         shared_ptr<const Image> in,
745         Crop crop,
746         dcp::Size inter_size,
747         dcp::Size out_size,
748         Scaler const * scaler
749         )
750         : _in (in)
751         , _crop (crop)
752         , _inter_size (inter_size)
753         , _out_size (out_size)
754         , _scaler (scaler)
755 {
756
757 }
758
759 void
760 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
761 {
762         _subtitle_image = image;
763         _subtitle_position = pos;
764 }
765
766 shared_ptr<Image>
767 PlayerImage::image (AVPixelFormat format, bool aligned)
768 {
769         shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
770         
771         Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
772
773         if (_subtitle_image) {
774                 out->alpha_blend (_subtitle_image, _subtitle_position);
775         }
776
777         return out;
778 }
779
780 void
781 PlayerStatistics::dump (shared_ptr<Log> log) const
782 {
783         log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
784         log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
785 }
786
787 PlayerStatistics const &
788 Player::statistics () const
789 {
790         return _statistics;
791 }