db8be20c2d2365d4a7eadf04c140efa7add20327
[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                         dec->set_dcp_times (_film->video_frame_rate(), _film->audio_frame_rate(), (*i)->frc, offset);
126                         DCPTime const t = dec->dcp_time - offset;
127                         if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) {
128                                 /* In the end-trimmed part; decoder has nothing else to give us */
129                                 dec.reset ();
130                                 done = true;
131                         } else if (t >= (*i)->content->trim_start ()) {
132                                 /* Within the un-trimmed part; everything's ok */
133                                 done = true;
134                         } else {
135                                 /* Within the start-trimmed part; get something else */
136                                 (*i)->decoder->consume ();
137                         }
138                 }
139
140                 if (!dec) {
141                         continue;
142                 }
143
144                 if (dec->dcp_time < earliest_time) {
145                         earliest_piece = *i;
146                         earliest_decoded = dec;
147                         earliest_time = dec->dcp_time;
148                 }
149
150                 if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
151                         earliest_audio = dec->dcp_time;
152                 }
153         }
154                 
155         if (!earliest_piece) {
156                 flush ();
157                 return true;
158         }
159
160         if (earliest_audio != TIME_MAX) {
161                 TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (max (int64_t (0), earliest_audio));
162                 Audio (tb.audio, tb.time);
163                 /* This assumes that the audio_frames_to_time conversion is exact
164                    so that there are no accumulated errors caused by rounding.
165                 */
166                 _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
167         }
168
169         /* Emit the earliest thing */
170
171         shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
172         shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
173         shared_ptr<DecodedImageSubtitle> dis = dynamic_pointer_cast<DecodedImageSubtitle> (earliest_decoded);
174         shared_ptr<DecodedTextSubtitle> dts = dynamic_pointer_cast<DecodedTextSubtitle> (earliest_decoded);
175
176 #if 0   
177         if (dv) {
178                 cout << "Video @ " << dv->dcp_time << " " << (double(dv->dcp_time) / TIME_HZ) << ".\n";
179         } else if (da) {
180                 cout << "Audio.\n";
181         } else if (dis) {
182                 cout << "Image sub.\n";
183         } else if (dts) {
184                 cout << "Text sub.\n";
185         }
186 #endif  
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                 (*i)->decoder->seek (ct, accurate);
436         }
437
438         _video_position = time_round_up (t, TIME_HZ / _film->video_frame_rate());
439         _audio_position = time_round_up (t, TIME_HZ / _film->audio_frame_rate());
440
441         _audio_merger.clear (_audio_position);
442
443         if (!accurate) {
444                 /* We just did an inaccurate seek, so it's likely that the next thing seen
445                    out of pass() will be a fair distance from _{video,audio}_position.  Setting
446                    this flag stops pass() from trying to fix that: we assume that if it
447                    was an inaccurate seek then the caller does not care too much about
448                    inserting black/silence to keep the time tidy.
449                 */
450                 _just_did_inaccurate_seek = true;
451         }
452 }
453
454 void
455 Player::setup_pieces ()
456 {
457         list<shared_ptr<Piece> > old_pieces = _pieces;
458         _pieces.clear ();
459
460         ContentList content = _playlist->content ();
461
462         for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
463
464                 if (!(*i)->paths_valid ()) {
465                         continue;
466                 }
467                 
468                 shared_ptr<Decoder> decoder;
469                 optional<FrameRateChange> frc;
470
471                 /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
472                 DCPTime best_overlap_t = 0;
473                 shared_ptr<VideoContent> best_overlap;
474                 for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
475                         shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
476                         if (!vc) {
477                                 continue;
478                         }
479                         
480                         DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
481                         if (overlap > best_overlap_t) {
482                                 best_overlap = vc;
483                                 best_overlap_t = overlap;
484                         }
485                 }
486
487                 optional<FrameRateChange> best_overlap_frc;
488                 if (best_overlap) {
489                         best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
490                 } else {
491                         /* No video overlap; e.g. if the DCP is just audio */
492                         best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
493                 }
494
495                 /* FFmpeg */
496                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
497                 if (fc) {
498                         decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
499                         frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
500                 }
501
502                 /* ImageContent */
503                 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
504                 if (ic) {
505                         /* See if we can re-use an old ImageDecoder */
506                         for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
507                                 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
508                                 if (imd && imd->content() == ic) {
509                                         decoder = imd;
510                                 }
511                         }
512
513                         if (!decoder) {
514                                 decoder.reset (new ImageDecoder (_film, ic));
515                         }
516
517                         frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
518                 }
519
520                 /* SndfileContent */
521                 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
522                 if (sc) {
523                         decoder.reset (new SndfileDecoder (_film, sc));
524                         frc = best_overlap_frc;
525                 }
526
527                 /* SubRipContent */
528                 shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
529                 if (rc) {
530                         decoder.reset (new SubRipDecoder (_film, rc));
531                         frc = best_overlap_frc;
532                 }
533
534                 ContentTime st = (*i)->trim_start() * frc->speed_up;
535                 decoder->seek (st, true);
536                 
537                 _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
538         }
539
540         _have_valid_pieces = true;
541
542         /* The Piece for the _last_incoming_video will no longer be valid */
543         _last_incoming_video.video.reset ();
544
545         _video_position = _audio_position = 0;
546 }
547
548 void
549 Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
550 {
551         shared_ptr<Content> c = w.lock ();
552         if (!c) {
553                 return;
554         }
555
556         if (
557                 property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
558                 property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
559                 property == VideoContentProperty::VIDEO_FRAME_TYPE 
560                 ) {
561                 
562                 _have_valid_pieces = false;
563                 Changed (frequent);
564
565         } else if (
566                 property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
567                 property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
568                 property == SubtitleContentProperty::SUBTITLE_SCALE
569                 ) {
570
571                 update_subtitle_from_image ();
572                 update_subtitle_from_text ();
573                 Changed (frequent);
574
575         } else if (
576                 property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
577                 property == VideoContentProperty::VIDEO_FRAME_RATE
578                 ) {
579                 
580                 Changed (frequent);
581
582         } else if (property == ContentProperty::PATH) {
583
584                 _have_valid_pieces = false;
585                 Changed (frequent);
586         }
587 }
588
589 void
590 Player::playlist_changed ()
591 {
592         _have_valid_pieces = false;
593         Changed (false);
594 }
595
596 void
597 Player::set_video_container_size (dcp::Size s)
598 {
599         _video_container_size = s;
600
601         shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
602         im->make_black ();
603         
604         _black_frame.reset (
605                 new PlayerImage (
606                         im,
607                         Crop(),
608                         _video_container_size,
609                         _video_container_size,
610                         Scaler::from_id ("bicubic")
611                         )
612                 );
613 }
614
615 void
616 Player::emit_black ()
617 {
618 #ifdef DCPOMATIC_DEBUG
619         _last_video.reset ();
620 #endif
621
622         Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
623         _video_position += _film->video_frames_to_time (1);
624         _last_emit_was_black = true;
625 }
626
627 void
628 Player::emit_silence (DCPTime most)
629 {
630         if (most == 0) {
631                 return;
632         }
633         
634         DCPTime t = min (most, TIME_HZ / 2);
635         shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t * _film->audio_frame_rate() / TIME_HZ));
636         silence->make_silent ();
637         Audio (silence, _audio_position);
638         
639         _audio_position += t;
640 }
641
642 void
643 Player::film_changed (Film::Property p)
644 {
645         /* Here we should notice Film properties that affect our output, and
646            alert listeners that our output now would be different to how it was
647            last time we were run.
648         */
649
650         if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
651                 Changed (false);
652         }
653 }
654
655 void
656 Player::update_subtitle_from_image ()
657 {
658         shared_ptr<Piece> piece = _image_subtitle.piece.lock ();
659         if (!piece) {
660                 return;
661         }
662
663         if (!_image_subtitle.subtitle->image) {
664                 _out_subtitle.image.reset ();
665                 return;
666         }
667
668         shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
669         assert (sc);
670
671         dcpomatic::Rect<double> in_rect = _image_subtitle.subtitle->rect;
672         dcp::Size scaled_size;
673
674         in_rect.x += sc->subtitle_x_offset ();
675         in_rect.y += sc->subtitle_y_offset ();
676
677         /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
678         scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale ();
679         scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale ();
680
681         /* Then we need a corrective translation, consisting of two parts:
682          *
683          * 1.  that which is the result of the scaling of the subtitle by _video_container_size; this will be
684          *     rect.x * _video_container_size.width and rect.y * _video_container_size.height.
685          *
686          * 2.  that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
687          *     (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
688          *     (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
689          *
690          * Combining these two translations gives these expressions.
691          */
692         
693         _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
694         _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
695         
696         _out_subtitle.image = _image_subtitle.subtitle->image->scale (
697                 scaled_size,
698                 Scaler::from_id ("bicubic"),
699                 _image_subtitle.subtitle->image->pixel_format (),
700                 true
701                 );
702         
703         _out_subtitle.from = _image_subtitle.subtitle->dcp_time + piece->content->position ();
704         _out_subtitle.to = _image_subtitle.subtitle->dcp_time_to + piece->content->position ();
705 }
706
707 /** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
708  *  @return false if this could not be done.
709  */
710 bool
711 Player::repeat_last_video ()
712 {
713         if (!_last_incoming_video.video || !_have_valid_pieces) {
714                 return false;
715         }
716
717         emit_video (
718                 _last_incoming_video.weak_piece,
719                 _last_incoming_video.video
720                 );
721
722         return true;
723 }
724
725 void
726 Player::update_subtitle_from_text ()
727 {
728         if (_text_subtitle.subtitle->subs.empty ()) {
729                 _out_subtitle.image.reset ();
730                 return;
731         }
732
733         render_subtitles (_text_subtitle.subtitle->subs, _video_container_size, _out_subtitle.image, _out_subtitle.position);
734 }
735
736 void
737 Player::set_approximate_size ()
738 {
739         _approximate_size = true;
740 }
741                               
742 PlayerImage::PlayerImage (
743         shared_ptr<const Image> in,
744         Crop crop,
745         dcp::Size inter_size,
746         dcp::Size out_size,
747         Scaler const * scaler
748         )
749         : _in (in)
750         , _crop (crop)
751         , _inter_size (inter_size)
752         , _out_size (out_size)
753         , _scaler (scaler)
754 {
755
756 }
757
758 void
759 PlayerImage::set_subtitle (shared_ptr<const Image> image, Position<int> pos)
760 {
761         _subtitle_image = image;
762         _subtitle_position = pos;
763 }
764
765 shared_ptr<Image>
766 PlayerImage::image (AVPixelFormat format, bool aligned)
767 {
768         shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
769         
770         Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
771
772         if (_subtitle_image) {
773                 out->alpha_blend (_subtitle_image, _subtitle_position);
774         }
775
776         return out;
777 }
778
779 void
780 PlayerStatistics::dump (shared_ptr<Log> log) const
781 {
782         log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
783         log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
784 }
785
786 PlayerStatistics const &
787 Player::statistics () const
788 {
789         return _statistics;
790 }