Fix some incorrect dynamic_pointer_casts.
[dcpomatic.git] / src / lib / player.cc
1 /*
2     Copyright (C) 2013-2016 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 "player.h"
21 #include "film.h"
22 #include "ffmpeg_decoder.h"
23 #include "video_decoder.h"
24 #include "audio_buffers.h"
25 #include "audio_content.h"
26 #include "ffmpeg_content.h"
27 #include "image_decoder.h"
28 #include "image_content.h"
29 #include "sndfile_decoder.h"
30 #include "sndfile_content.h"
31 #include "subtitle_content.h"
32 #include "text_subtitle_decoder.h"
33 #include "text_subtitle_content.h"
34 #include "dcp_content.h"
35 #include "job.h"
36 #include "image.h"
37 #include "raw_image_proxy.h"
38 #include "ratio.h"
39 #include "log.h"
40 #include "render_subtitles.h"
41 #include "config.h"
42 #include "content_video.h"
43 #include "player_video.h"
44 #include "frame_rate_change.h"
45 #include "dcp_content.h"
46 #include "dcp_decoder.h"
47 #include "dcp_subtitle_content.h"
48 #include "dcp_subtitle_decoder.h"
49 #include "audio_processor.h"
50 #include "playlist.h"
51 #include "referenced_reel_asset.h"
52 #include <dcp/reel.h>
53 #include <dcp/reel_sound_asset.h>
54 #include <dcp/reel_subtitle_asset.h>
55 #include <dcp/reel_picture_asset.h>
56 #include <boost/foreach.hpp>
57 #include <stdint.h>
58 #include <algorithm>
59 #include <iostream>
60
61 #include "i18n.h"
62
63 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
64
65 using std::list;
66 using std::cout;
67 using std::min;
68 using std::max;
69 using std::min;
70 using std::vector;
71 using std::pair;
72 using std::map;
73 using std::make_pair;
74 using std::copy;
75 using boost::shared_ptr;
76 using boost::weak_ptr;
77 using boost::dynamic_pointer_cast;
78 using boost::optional;
79 using boost::scoped_ptr;
80
81 static bool
82 has_video (Content* c)
83 {
84         return static_cast<bool>(c->video);
85 }
86
87 static bool
88 has_audio (Content* c)
89 {
90         return static_cast<bool>(c->audio);
91 }
92
93 static bool
94 has_subtitle (Content* c)
95 {
96         return static_cast<bool>(c->subtitle);
97 }
98
99 Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist)
100         : _film (film)
101         , _playlist (playlist)
102         , _have_valid_pieces (false)
103         , _ignore_video (false)
104         , _ignore_audio (false)
105         , _always_burn_subtitles (false)
106         , _fast (false)
107         , _play_referenced (false)
108 {
109         _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
110         _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
111         _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::playlist_content_changed, this, _1, _2, _3));
112         set_video_container_size (_film->frame_size ());
113
114         film_changed (Film::AUDIO_PROCESSOR);
115 }
116
117 void
118 Player::setup_pieces ()
119 {
120         list<shared_ptr<Piece> > old_pieces = _pieces;
121         _pieces.clear ();
122
123         BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
124
125                 if (!i->paths_valid ()) {
126                         continue;
127                 }
128
129                 shared_ptr<Decoder> decoder;
130                 optional<FrameRateChange> frc;
131
132                 /* FFmpeg */
133                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (i);
134                 if (fc) {
135                         decoder.reset (new FFmpegDecoder (fc, _film->log(), _fast));
136                         frc = FrameRateChange (fc->active_video_frame_rate(), _film->video_frame_rate());
137                 }
138
139                 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
140                 if (dc) {
141                         decoder.reset (new DCPDecoder (dc, _film->log(), _fast));
142                         frc = FrameRateChange (dc->active_video_frame_rate(), _film->video_frame_rate());
143                 }
144
145                 /* ImageContent */
146                 shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (i);
147                 if (ic) {
148                         /* See if we can re-use an old ImageDecoder */
149                         for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
150                                 shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
151                                 if (imd && imd->content() == ic) {
152                                         decoder = imd;
153                                 }
154                         }
155
156                         if (!decoder) {
157                                 decoder.reset (new ImageDecoder (ic, _film->log()));
158                         }
159
160                         frc = FrameRateChange (ic->active_video_frame_rate(), _film->video_frame_rate());
161                 }
162
163                 /* SndfileContent */
164                 shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (i);
165                 if (sc) {
166                         decoder.reset (new SndfileDecoder (sc, _fast, _film->log()));
167
168                         /* Work out a FrameRateChange for the best overlap video for this content */
169                         DCPTime best_overlap_t;
170                         shared_ptr<Content> best_overlap;
171                         BOOST_FOREACH (shared_ptr<Content> j, _playlist->content ()) {
172                                 if (!j->video) {
173                                         continue;
174                                 }
175
176                                 DCPTime const overlap = min (j->end(), i->end()) - max (j->position(), i->position());
177                                 if (overlap > best_overlap_t) {
178                                         best_overlap = j;
179                                         best_overlap_t = overlap;
180                                 }
181                         }
182
183                         if (best_overlap) {
184                                 frc = FrameRateChange (best_overlap->active_video_frame_rate(), _film->video_frame_rate ());
185                         } else {
186                                 /* No video overlap; e.g. if the DCP is just audio */
187                                 frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
188                         }
189                 }
190
191                 /* It's questionable whether subtitle content should have a video frame rate; perhaps
192                    it should be assumed that any subtitle content has been prepared at the same rate
193                    as simultaneous video content (like we do with audio).
194                 */
195
196                 /* TextSubtitleContent */
197                 shared_ptr<const TextSubtitleContent> rc = dynamic_pointer_cast<const TextSubtitleContent> (i);
198                 if (rc) {
199                         decoder.reset (new TextSubtitleDecoder (rc));
200                         frc = FrameRateChange (rc->active_video_frame_rate(), _film->video_frame_rate());
201                 }
202
203                 /* DCPSubtitleContent */
204                 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (i);
205                 if (dsc) {
206                         decoder.reset (new DCPSubtitleDecoder (dsc));
207                         frc = FrameRateChange (dsc->active_video_frame_rate(), _film->video_frame_rate());
208                 }
209
210                 if (decoder->video && _ignore_video) {
211                         decoder->video->set_ignore ();
212                 }
213
214                 if (decoder->audio && _ignore_audio) {
215                         decoder->audio->set_ignore ();
216                 }
217
218                 _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc.get ())));
219         }
220
221         _have_valid_pieces = true;
222 }
223
224 void
225 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
226 {
227         shared_ptr<Content> c = w.lock ();
228         if (!c) {
229                 return;
230         }
231
232         if (
233                 property == ContentProperty::POSITION ||
234                 property == ContentProperty::LENGTH ||
235                 property == ContentProperty::TRIM_START ||
236                 property == ContentProperty::TRIM_END ||
237                 property == ContentProperty::PATH ||
238                 property == VideoContentProperty::FRAME_TYPE ||
239                 property == DCPContentProperty::CAN_BE_PLAYED ||
240                 property == SubtitleContentProperty::COLOUR ||
241                 property == SubtitleContentProperty::OUTLINE ||
242                 property == SubtitleContentProperty::OUTLINE_COLOUR ||
243                 property == FFmpegContentProperty::SUBTITLE_STREAM
244                 ) {
245
246                 _have_valid_pieces = false;
247                 Changed (frequent);
248
249         } else if (
250                 property == ContentProperty::VIDEO_FRAME_RATE ||
251                 property == SubtitleContentProperty::USE ||
252                 property == SubtitleContentProperty::X_OFFSET ||
253                 property == SubtitleContentProperty::Y_OFFSET ||
254                 property == SubtitleContentProperty::X_SCALE ||
255                 property == SubtitleContentProperty::Y_SCALE ||
256                 property == SubtitleContentProperty::FONTS ||
257                 property == VideoContentProperty::CROP ||
258                 property == VideoContentProperty::SCALE ||
259                 property == VideoContentProperty::FADE_IN ||
260                 property == VideoContentProperty::FADE_OUT ||
261                 property == VideoContentProperty::COLOUR_CONVERSION
262                 ) {
263
264                 Changed (frequent);
265         }
266 }
267
268 void
269 Player::set_video_container_size (dcp::Size s)
270 {
271         _video_container_size = s;
272
273         _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
274         _black_image->make_black ();
275 }
276
277 void
278 Player::playlist_changed ()
279 {
280         _have_valid_pieces = false;
281         Changed (false);
282 }
283
284 void
285 Player::film_changed (Film::Property p)
286 {
287         /* Here we should notice Film properties that affect our output, and
288            alert listeners that our output now would be different to how it was
289            last time we were run.
290         */
291
292         if (p == Film::CONTAINER) {
293                 Changed (false);
294         } else if (p == Film::VIDEO_FRAME_RATE) {
295                 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
296                    so we need new pieces here.
297                 */
298                 _have_valid_pieces = false;
299                 Changed (false);
300         } else if (p == Film::AUDIO_PROCESSOR) {
301                 if (_film->audio_processor ()) {
302                         _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
303                 }
304         }
305 }
306
307 list<PositionImage>
308 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
309 {
310         list<PositionImage> all;
311
312         for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
313                 if (!i->image) {
314                         continue;
315                 }
316
317                 /* We will scale the subtitle up to fit _video_container_size */
318                 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
319
320                 /* Then we need a corrective translation, consisting of two parts:
321                  *
322                  * 1.  that which is the result of the scaling of the subtitle by _video_container_size; this will be
323                  *     rect.x * _video_container_size.width and rect.y * _video_container_size.height.
324                  *
325                  * 2.  that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
326                  *     (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
327                  *     (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
328                  *
329                  * Combining these two translations gives these expressions.
330                  */
331
332                 all.push_back (
333                         PositionImage (
334                                 i->image->scale (
335                                         scaled_size,
336                                         dcp::YUV_TO_RGB_REC601,
337                                         i->image->pixel_format (),
338                                         true,
339                                         _fast
340                                         ),
341                                 Position<int> (
342                                         lrint (_video_container_size.width * i->rectangle.x),
343                                         lrint (_video_container_size.height * i->rectangle.y)
344                                         )
345                                 )
346                         );
347         }
348
349         return all;
350 }
351
352 shared_ptr<PlayerVideo>
353 Player::black_player_video_frame (DCPTime time) const
354 {
355         return shared_ptr<PlayerVideo> (
356                 new PlayerVideo (
357                         shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
358                         time,
359                         Crop (),
360                         optional<double> (),
361                         _video_container_size,
362                         _video_container_size,
363                         EYES_BOTH,
364                         PART_WHOLE,
365                         PresetColourConversion::all().front().conversion
366                 )
367         );
368 }
369
370 /** @return All PlayerVideos at the given time.  There may be none if the content
371  *  at `time' is a DCP which we are passing through (i.e. referring to by reference)
372  *  or 2 if we have 3D.
373  */
374 list<shared_ptr<PlayerVideo> >
375 Player::get_video (DCPTime time, bool accurate)
376 {
377         if (!_have_valid_pieces) {
378                 setup_pieces ();
379         }
380
381         /* Find subtitles for possible burn-in */
382
383         PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false, true, accurate);
384
385         list<PositionImage> sub_images;
386
387         /* Image subtitles */
388         list<PositionImage> c = transform_image_subtitles (ps.image);
389         copy (c.begin(), c.end(), back_inserter (sub_images));
390
391         /* Text subtitles (rendered to an image) */
392         if (!ps.text.empty ()) {
393                 list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size);
394                 copy (s.begin (), s.end (), back_inserter (sub_images));
395         }
396
397         optional<PositionImage> subtitles;
398         if (!sub_images.empty ()) {
399                 subtitles = merge (sub_images);
400         }
401
402         /* Find pieces containing video which is happening now */
403
404         list<shared_ptr<Piece> > ov = overlaps (
405                 time,
406                 time + DCPTime::from_frames (1, _film->video_frame_rate ()),
407                 &has_video
408                 );
409
410         list<shared_ptr<PlayerVideo> > pvf;
411
412         if (ov.empty ()) {
413                 /* No video content at this time */
414                 pvf.push_back (black_player_video_frame (time));
415         } else {
416                 /* Some video content at this time */
417                 shared_ptr<Piece> last = *(ov.rbegin ());
418                 VideoFrameType const last_type = last->content->video->frame_type ();
419
420                 /* Get video from appropriate piece(s) */
421                 BOOST_FOREACH (shared_ptr<Piece> piece, ov) {
422
423                         shared_ptr<VideoDecoder> decoder = piece->decoder->video;
424                         DCPOMATIC_ASSERT (decoder);
425
426                         shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (piece->content);
427                         if (dcp_content && dcp_content->reference_video () && !_play_referenced) {
428                                 continue;
429                         }
430
431                         bool const use =
432                                 /* always use the last video */
433                                 piece == last ||
434                                 /* with a corresponding L/R eye if appropriate */
435                                 (last_type == VIDEO_FRAME_TYPE_3D_LEFT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) ||
436                                 (last_type == VIDEO_FRAME_TYPE_3D_RIGHT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT);
437
438                         if (use) {
439                                 /* We want to use this piece */
440                                 list<ContentVideo> content_video = decoder->get (dcp_to_content_video (piece, time), accurate);
441                                 if (content_video.empty ()) {
442                                         pvf.push_back (black_player_video_frame (time));
443                                 } else {
444                                         dcp::Size image_size = piece->content->video->scale().size (
445                                                 piece->content->video, _video_container_size, _film->frame_size ()
446                                                 );
447
448                                         for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
449                                                 pvf.push_back (
450                                                         shared_ptr<PlayerVideo> (
451                                                                 new PlayerVideo (
452                                                                         i->image,
453                                                                         content_video_to_dcp (piece, i->frame),
454                                                                         piece->content->video->crop (),
455                                                                         piece->content->video->fade (i->frame),
456                                                                         image_size,
457                                                                         _video_container_size,
458                                                                         i->eyes,
459                                                                         i->part,
460                                                                         piece->content->video->colour_conversion ()
461                                                                         )
462                                                                 )
463                                                         );
464                                         }
465                                 }
466                         } else {
467                                 /* Discard unused video */
468                                 decoder->get (dcp_to_content_video (piece, time), accurate);
469                         }
470                 }
471         }
472
473         if (subtitles) {
474                 BOOST_FOREACH (shared_ptr<PlayerVideo> p, pvf) {
475                         p->set_subtitle (subtitles.get ());
476                 }
477         }
478
479         return pvf;
480 }
481
482 /** @return Audio data or 0 if the only audio data here is referenced DCP data */
483 shared_ptr<AudioBuffers>
484 Player::get_audio (DCPTime time, DCPTime length, bool accurate)
485 {
486         if (!_have_valid_pieces) {
487                 setup_pieces ();
488         }
489
490         Frame const length_frames = length.frames_round (_film->audio_frame_rate ());
491
492         shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
493         audio->make_silent ();
494
495         list<shared_ptr<Piece> > ov = overlaps (time, time + length, has_audio);
496         if (ov.empty ()) {
497                 return audio;
498         }
499
500         bool all_referenced = true;
501         BOOST_FOREACH (shared_ptr<Piece> i, ov) {
502                 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (i->content);
503                 if (i->content->audio && (!dcp_content || !dcp_content->reference_audio ())) {
504                         /* There is audio content which is not from a DCP or not set to be referenced */
505                         all_referenced = false;
506                 }
507         }
508
509         if (all_referenced && !_play_referenced) {
510                 return shared_ptr<AudioBuffers> ();
511         }
512
513         BOOST_FOREACH (shared_ptr<Piece> i, ov) {
514
515                 DCPOMATIC_ASSERT (i->content->audio);
516                 shared_ptr<AudioDecoder> decoder = i->decoder->audio;
517                 DCPOMATIC_ASSERT (decoder);
518
519                 /* The time that we should request from the content */
520                 DCPTime request = time - DCPTime::from_seconds (i->content->audio->delay() / 1000.0);
521                 Frame request_frames = length_frames;
522                 DCPTime offset;
523                 if (request < DCPTime ()) {
524                         /* We went off the start of the content, so we will need to offset
525                            the stuff we get back.
526                         */
527                         offset = -request;
528                         request_frames += request.frames_round (_film->audio_frame_rate ());
529                         if (request_frames < 0) {
530                                 request_frames = 0;
531                         }
532                         request = DCPTime ();
533                 }
534
535                 Frame const content_frame = dcp_to_resampled_audio (i, request);
536
537                 BOOST_FOREACH (AudioStreamPtr j, i->content->audio->streams ()) {
538
539                         if (j->channels() == 0) {
540                                 /* Some content (e.g. DCPs) can have streams with no channels */
541                                 continue;
542                         }
543
544                         /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
545                         ContentAudio all = decoder->get (j, content_frame, request_frames, accurate);
546
547                         /* Gain */
548                         if (i->content->audio->gain() != 0) {
549                                 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
550                                 gain->apply_gain (i->content->audio->gain ());
551                                 all.audio = gain;
552                         }
553
554                         /* Remap channels */
555                         shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
556                         dcp_mapped->make_silent ();
557                         AudioMapping map = j->mapping ();
558                         for (int i = 0; i < map.input_channels(); ++i) {
559                                 for (int j = 0; j < _film->audio_channels(); ++j) {
560                                         if (map.get (i, j) > 0) {
561                                                 dcp_mapped->accumulate_channel (
562                                                         all.audio.get(),
563                                                         i,
564                                                         j,
565                                                         map.get (i, j)
566                                                         );
567                                         }
568                                 }
569                         }
570
571                         if (_audio_processor) {
572                                 dcp_mapped = _audio_processor->run (dcp_mapped, _film->audio_channels ());
573                         }
574
575                         all.audio = dcp_mapped;
576
577                         audio->accumulate_frames (
578                                 all.audio.get(),
579                                 content_frame - all.frame,
580                                 offset.frames_round (_film->audio_frame_rate()),
581                                 min (Frame (all.audio->frames()), request_frames)
582                                 );
583                 }
584         }
585
586         return audio;
587 }
588
589 Frame
590 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
591 {
592         DCPTime s = t - piece->content->position ();
593         s = min (piece->content->length_after_trim(), s);
594         s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
595
596         /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
597            then convert that ContentTime to frames at the content's rate.  However this fails for
598            situations like content at 29.9978733fps, DCP at 30fps.  The accuracy of the Time type is not
599            enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
600
601            Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
602         */
603         return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
604 }
605
606 DCPTime
607 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
608 {
609         /* See comment in dcp_to_content_video */
610         DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
611         return max (DCPTime (), d + piece->content->position ());
612 }
613
614 Frame
615 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
616 {
617         DCPTime s = t - piece->content->position ();
618         s = min (piece->content->length_after_trim(), s);
619         /* See notes in dcp_to_content_video */
620         return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
621 }
622
623 ContentTime
624 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
625 {
626         DCPTime s = t - piece->content->position ();
627         s = min (piece->content->length_after_trim(), s);
628         return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
629 }
630
631 DCPTime
632 Player::content_subtitle_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
633 {
634         return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
635 }
636
637 /** @param burnt true to return only subtitles to be burnt, false to return only
638  *  subtitles that should not be burnt.  This parameter will be ignored if
639  *  _always_burn_subtitles is true; in this case, all subtitles will be returned.
640  */
641 PlayerSubtitles
642 Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt, bool accurate)
643 {
644         list<shared_ptr<Piece> > subs = overlaps (time, time + length, has_subtitle);
645
646         PlayerSubtitles ps (time, length);
647
648         for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
649                 if (!(*j)->content->subtitle->use () || (!_always_burn_subtitles && (burnt != (*j)->content->subtitle->burn ()))) {
650                         continue;
651                 }
652
653                 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> ((*j)->content);
654                 if (dcp_content && dcp_content->reference_subtitle () && !_play_referenced) {
655                         continue;
656                 }
657
658                 shared_ptr<SubtitleDecoder> subtitle_decoder = (*j)->decoder->subtitle;
659                 ContentTime const from = dcp_to_content_subtitle (*j, time);
660                 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
661                 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
662
663                 list<ContentImageSubtitle> image = subtitle_decoder->get_image (ContentTimePeriod (from, to), starting, accurate);
664                 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
665
666                         /* Apply content's subtitle offsets */
667                         i->sub.rectangle.x += (*j)->content->subtitle->x_offset ();
668                         i->sub.rectangle.y += (*j)->content->subtitle->y_offset ();
669
670                         /* Apply content's subtitle scale */
671                         i->sub.rectangle.width *= (*j)->content->subtitle->x_scale ();
672                         i->sub.rectangle.height *= (*j)->content->subtitle->y_scale ();
673
674                         /* Apply a corrective translation to keep the subtitle centred after that scale */
675                         i->sub.rectangle.x -= i->sub.rectangle.width * ((*j)->content->subtitle->x_scale() - 1);
676                         i->sub.rectangle.y -= i->sub.rectangle.height * ((*j)->content->subtitle->y_scale() - 1);
677
678                         ps.image.push_back (i->sub);
679                 }
680
681                 list<ContentTextSubtitle> text = subtitle_decoder->get_text (ContentTimePeriod (from, to), starting, accurate);
682                 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
683                         BOOST_FOREACH (dcp::SubtitleString s, ts.subs) {
684                                 s.set_h_position (s.h_position() + (*j)->content->subtitle->x_offset ());
685                                 s.set_v_position (s.v_position() + (*j)->content->subtitle->y_offset ());
686                                 float const xs = (*j)->content->subtitle->x_scale();
687                                 float const ys = (*j)->content->subtitle->y_scale();
688                                 float size = s.size();
689
690                                 /* Adjust size to express the common part of the scaling;
691                                    e.g. if xs = ys = 0.5 we scale size by 2.
692                                 */
693                                 if (xs > 1e-5 && ys > 1e-5) {
694                                         size *= 1 / min (1 / xs, 1 / ys);
695                                 }
696                                 s.set_size (size);
697
698                                 /* Then express aspect ratio changes */
699                                 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
700                                         s.set_aspect_adjust (xs / ys);
701                                 }
702                                 s.set_in (dcp::Time(content_subtitle_to_dcp (*j, ts.period().from).seconds(), 1000));
703                                 s.set_out (dcp::Time(content_subtitle_to_dcp (*j, ts.period().to).seconds(), 1000));
704                                 ps.text.push_back (s);
705                                 ps.add_fonts ((*j)->content->subtitle->fonts ());
706                         }
707                 }
708         }
709
710         return ps;
711 }
712
713 list<shared_ptr<Font> >
714 Player::get_subtitle_fonts ()
715 {
716         if (!_have_valid_pieces) {
717                 setup_pieces ();
718         }
719
720         list<shared_ptr<Font> > fonts;
721         BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
722                 if (p->content->subtitle) {
723                         /* XXX: things may go wrong if there are duplicate font IDs
724                            with different font files.
725                         */
726                         list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
727                         copy (f.begin(), f.end(), back_inserter (fonts));
728                 }
729         }
730
731         return fonts;
732 }
733
734 /** Set this player never to produce any video data */
735 void
736 Player::set_ignore_video ()
737 {
738         _ignore_video = true;
739 }
740
741 /** Set this player never to produce any audio data */
742 void
743 Player::set_ignore_audio ()
744 {
745         _ignore_audio = true;
746 }
747
748 /** Set whether or not this player should always burn text subtitles into the image,
749  *  regardless of the content settings.
750  *  @param burn true to always burn subtitles, false to obey content settings.
751  */
752 void
753 Player::set_always_burn_subtitles (bool burn)
754 {
755         _always_burn_subtitles = burn;
756 }
757
758 void
759 Player::set_fast ()
760 {
761         _fast = true;
762         _have_valid_pieces = false;
763 }
764
765 void
766 Player::set_play_referenced ()
767 {
768         _play_referenced = true;
769         _have_valid_pieces = false;
770 }
771
772 list<ReferencedReelAsset>
773 Player::get_reel_assets ()
774 {
775         list<ReferencedReelAsset> a;
776
777         BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
778                 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
779                 if (!j) {
780                         continue;
781                 }
782
783                 scoped_ptr<DCPDecoder> decoder;
784                 try {
785                         decoder.reset (new DCPDecoder (j, _film->log(), false));
786                 } catch (...) {
787                         return a;
788                 }
789
790                 int64_t offset = 0;
791                 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
792                         DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
793                         if (j->reference_video ()) {
794                                 a.push_back (
795                                         ReferencedReelAsset (
796                                                 k->main_picture (),
797                                                 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_picture()->duration(), _film->video_frame_rate()))
798                                                 )
799                                         );
800                         }
801
802                         if (j->reference_audio ()) {
803                                 a.push_back (
804                                         ReferencedReelAsset (
805                                                 k->main_sound (),
806                                                 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_sound()->duration(), _film->video_frame_rate()))
807                                                 )
808                                         );
809                         }
810
811                         if (j->reference_subtitle ()) {
812                                 DCPOMATIC_ASSERT (k->main_subtitle ());
813                                 a.push_back (
814                                         ReferencedReelAsset (
815                                                 k->main_subtitle (),
816                                                 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_subtitle()->duration(), _film->video_frame_rate()))
817                                                 )
818                                         );
819                         }
820
821                         /* Assume that main picture duration is the length of the reel */
822                         offset += k->main_picture()->duration ();
823                 }
824         }
825
826         return a;
827 }
828
829 list<shared_ptr<Piece> >
830 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
831 {
832         if (!_have_valid_pieces) {
833                 setup_pieces ();
834         }
835
836         list<shared_ptr<Piece> > overlaps;
837         BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
838                 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
839                         overlaps.push_back (i);
840                 }
841         }
842
843         return overlaps;
844 }