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