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