Remove Sndfile code and use FFmpeg instead.
[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_decoder.h"
25 #include "audio_buffers.h"
26 #include "audio_content.h"
27 #include "ffmpeg_content.h"
28 #include "image_decoder.h"
29 #include "content_audio.h"
30 #include "image_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                 /* It's questionable whether subtitle content should have a video frame rate; perhaps
164                    it should be assumed that any subtitle content has been prepared at the same rate
165                    as simultaneous video content (like we do with audio).
166                 */
167
168                 /* TextSubtitleContent */
169                 shared_ptr<const TextSubtitleContent> rc = dynamic_pointer_cast<const TextSubtitleContent> (i);
170                 if (rc) {
171                         decoder.reset (new TextSubtitleDecoder (rc));
172                         frc = FrameRateChange (rc->active_video_frame_rate(), _film->video_frame_rate());
173                 }
174
175                 /* DCPSubtitleContent */
176                 shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (i);
177                 if (dsc) {
178                         decoder.reset (new DCPSubtitleDecoder (dsc));
179                         frc = FrameRateChange (dsc->active_video_frame_rate(), _film->video_frame_rate());
180                 }
181
182                 if (decoder->video && _ignore_video) {
183                         decoder->video->set_ignore ();
184                 }
185
186                 if (decoder->audio && _ignore_audio) {
187                         decoder->audio->set_ignore ();
188                 }
189
190                 _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc.get ())));
191         }
192
193         _have_valid_pieces = true;
194 }
195
196 void
197 Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
198 {
199         shared_ptr<Content> c = w.lock ();
200         if (!c) {
201                 return;
202         }
203
204         if (
205                 property == ContentProperty::POSITION ||
206                 property == ContentProperty::LENGTH ||
207                 property == ContentProperty::TRIM_START ||
208                 property == ContentProperty::TRIM_END ||
209                 property == ContentProperty::PATH ||
210                 property == VideoContentProperty::FRAME_TYPE ||
211                 property == DCPContentProperty::CAN_BE_PLAYED ||
212                 property == SubtitleContentProperty::COLOUR ||
213                 property == SubtitleContentProperty::OUTLINE ||
214                 property == SubtitleContentProperty::OUTLINE_COLOUR ||
215                 property == FFmpegContentProperty::SUBTITLE_STREAM
216                 ) {
217
218                 _have_valid_pieces = false;
219                 Changed (frequent);
220
221         } else if (
222                 property == ContentProperty::VIDEO_FRAME_RATE ||
223                 property == SubtitleContentProperty::USE ||
224                 property == SubtitleContentProperty::X_OFFSET ||
225                 property == SubtitleContentProperty::Y_OFFSET ||
226                 property == SubtitleContentProperty::X_SCALE ||
227                 property == SubtitleContentProperty::Y_SCALE ||
228                 property == SubtitleContentProperty::FONTS ||
229                 property == VideoContentProperty::CROP ||
230                 property == VideoContentProperty::SCALE ||
231                 property == VideoContentProperty::FADE_IN ||
232                 property == VideoContentProperty::FADE_OUT ||
233                 property == VideoContentProperty::COLOUR_CONVERSION
234                 ) {
235
236                 Changed (frequent);
237         }
238 }
239
240 void
241 Player::set_video_container_size (dcp::Size s)
242 {
243         _video_container_size = s;
244
245         _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
246         _black_image->make_black ();
247 }
248
249 void
250 Player::playlist_changed ()
251 {
252         _have_valid_pieces = false;
253         Changed (false);
254 }
255
256 void
257 Player::film_changed (Film::Property p)
258 {
259         /* Here we should notice Film properties that affect our output, and
260            alert listeners that our output now would be different to how it was
261            last time we were run.
262         */
263
264         if (p == Film::CONTAINER) {
265                 Changed (false);
266         } else if (p == Film::VIDEO_FRAME_RATE) {
267                 /* Pieces contain a FrameRateChange which contains the DCP frame rate,
268                    so we need new pieces here.
269                 */
270                 _have_valid_pieces = false;
271                 Changed (false);
272         } else if (p == Film::AUDIO_PROCESSOR) {
273                 if (_film->audio_processor ()) {
274                         _audio_processor = _film->audio_processor()->clone (_film->audio_frame_rate ());
275                 }
276         }
277 }
278
279 list<PositionImage>
280 Player::transform_image_subtitles (list<ImageSubtitle> subs) const
281 {
282         list<PositionImage> all;
283
284         for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
285                 if (!i->image) {
286                         continue;
287                 }
288
289                 /* We will scale the subtitle up to fit _video_container_size */
290                 dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
291
292                 /* Then we need a corrective translation, consisting of two parts:
293                  *
294                  * 1.  that which is the result of the scaling of the subtitle by _video_container_size; this will be
295                  *     rect.x * _video_container_size.width and rect.y * _video_container_size.height.
296                  *
297                  * 2.  that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
298                  *     (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
299                  *     (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
300                  *
301                  * Combining these two translations gives these expressions.
302                  */
303
304                 all.push_back (
305                         PositionImage (
306                                 i->image->scale (
307                                         scaled_size,
308                                         dcp::YUV_TO_RGB_REC601,
309                                         i->image->pixel_format (),
310                                         true,
311                                         _fast
312                                         ),
313                                 Position<int> (
314                                         lrint (_video_container_size.width * i->rectangle.x),
315                                         lrint (_video_container_size.height * i->rectangle.y)
316                                         )
317                                 )
318                         );
319         }
320
321         return all;
322 }
323
324 shared_ptr<PlayerVideo>
325 Player::black_player_video_frame (DCPTime time) const
326 {
327         return shared_ptr<PlayerVideo> (
328                 new PlayerVideo (
329                         shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
330                         time,
331                         Crop (),
332                         optional<double> (),
333                         _video_container_size,
334                         _video_container_size,
335                         EYES_BOTH,
336                         PART_WHOLE,
337                         PresetColourConversion::all().front().conversion
338                 )
339         );
340 }
341
342 /** @return All PlayerVideos at the given time.  There may be none if the content
343  *  at `time' is a DCP which we are passing through (i.e. referring to by reference)
344  *  or 2 if we have 3D.
345  */
346 list<shared_ptr<PlayerVideo> >
347 Player::get_video (DCPTime time, bool accurate)
348 {
349         if (!_have_valid_pieces) {
350                 setup_pieces ();
351         }
352
353         /* Find subtitles for possible burn-in */
354
355         PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false, true, accurate);
356
357         list<PositionImage> sub_images;
358
359         /* Image subtitles */
360         list<PositionImage> c = transform_image_subtitles (ps.image);
361         copy (c.begin(), c.end(), back_inserter (sub_images));
362
363         /* Text subtitles (rendered to an image) */
364         if (!ps.text.empty ()) {
365                 list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size);
366                 copy (s.begin (), s.end (), back_inserter (sub_images));
367         }
368
369         optional<PositionImage> subtitles;
370         if (!sub_images.empty ()) {
371                 subtitles = merge (sub_images);
372         }
373
374         /* Find pieces containing video which is happening now */
375
376         list<shared_ptr<Piece> > ov = overlaps (
377                 time,
378                 time + DCPTime::from_frames (1, _film->video_frame_rate ()),
379                 &has_video
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 = 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 = piece->decoder->video;
396                         DCPOMATIC_ASSERT (decoder);
397
398                         shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (piece->content);
399                         if (dcp_content && dcp_content->reference_video () && !_play_referenced) {
400                                 continue;
401                         }
402
403                         bool const use =
404                                 /* always use the last video */
405                                 piece == last ||
406                                 /* with a corresponding L/R eye if appropriate */
407                                 (last_type == VIDEO_FRAME_TYPE_3D_LEFT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) ||
408                                 (last_type == VIDEO_FRAME_TYPE_3D_RIGHT && piece->content->video->frame_type() == VIDEO_FRAME_TYPE_3D_LEFT);
409
410                         if (use) {
411                                 /* We want to use this piece */
412                                 list<ContentVideo> content_video = decoder->get (dcp_to_content_video (piece, time), accurate);
413                                 if (content_video.empty ()) {
414                                         pvf.push_back (black_player_video_frame (time));
415                                 } else {
416                                         dcp::Size image_size = piece->content->video->scale().size (
417                                                 piece->content->video, _video_container_size, _film->frame_size ()
418                                                 );
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                                                                         piece->content->video->crop (),
427                                                                         piece->content->video->fade (i->frame),
428                                                                         image_size,
429                                                                         _video_container_size,
430                                                                         i->eyes,
431                                                                         i->part,
432                                                                         piece->content->video->colour_conversion ()
433                                                                         )
434                                                                 )
435                                                         );
436                                         }
437                                 }
438                         } else {
439                                 /* Discard unused video */
440                                 decoder->get (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 (time, time + length, has_audio);
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<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (i->content);
475                 if (i->content->audio && (!dcp_content || !dcp_content->reference_audio ())) {
476                         /* There is audio content which is not from a DCP or not set to be referenced */
477                         all_referenced = false;
478                 }
479         }
480
481         if (all_referenced && !_play_referenced) {
482                 return shared_ptr<AudioBuffers> ();
483         }
484
485         BOOST_FOREACH (shared_ptr<Piece> i, ov) {
486
487                 DCPOMATIC_ASSERT (i->content->audio);
488                 shared_ptr<AudioDecoder> decoder = i->decoder->audio;
489                 DCPOMATIC_ASSERT (decoder);
490
491                 /* The time that we should request from the content */
492                 DCPTime request = time - DCPTime::from_seconds (i->content->audio->delay() / 1000.0);
493                 Frame request_frames = length_frames;
494                 DCPTime offset;
495                 if (request < DCPTime ()) {
496                         /* We went off the start of the content, so we will need to offset
497                            the stuff we get back.
498                         */
499                         offset = -request;
500                         request_frames += request.frames_round (_film->audio_frame_rate ());
501                         if (request_frames < 0) {
502                                 request_frames = 0;
503                         }
504                         request = DCPTime ();
505                 }
506
507                 Frame const content_frame = dcp_to_resampled_audio (i, request);
508
509                 BOOST_FOREACH (AudioStreamPtr j, i->content->audio->streams ()) {
510
511                         if (j->channels() == 0) {
512                                 /* Some content (e.g. DCPs) can have streams with no channels */
513                                 continue;
514                         }
515
516                         /* Audio from this piece's decoder stream (which might be more or less than what we asked for) */
517                         ContentAudio all = decoder->get (j, content_frame, request_frames, accurate);
518
519                         /* Gain */
520                         if (i->content->audio->gain() != 0) {
521                                 shared_ptr<AudioBuffers> gain (new AudioBuffers (all.audio));
522                                 gain->apply_gain (i->content->audio->gain ());
523                                 all.audio = gain;
524                         }
525
526                         /* Remap channels */
527                         shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames()));
528                         dcp_mapped->make_silent ();
529                         AudioMapping map = j->mapping ();
530                         for (int i = 0; i < map.input_channels(); ++i) {
531                                 for (int j = 0; j < _film->audio_channels(); ++j) {
532                                         if (map.get (i, j) > 0) {
533                                                 dcp_mapped->accumulate_channel (
534                                                         all.audio.get(),
535                                                         i,
536                                                         j,
537                                                         map.get (i, j)
538                                                         );
539                                         }
540                                 }
541                         }
542
543                         if (_audio_processor) {
544                                 dcp_mapped = _audio_processor->run (dcp_mapped, _film->audio_channels ());
545                         }
546
547                         all.audio = dcp_mapped;
548
549                         audio->accumulate_frames (
550                                 all.audio.get(),
551                                 content_frame - all.frame,
552                                 offset.frames_round (_film->audio_frame_rate()),
553                                 min (Frame (all.audio->frames()), request_frames)
554                                 );
555                 }
556         }
557
558         return audio;
559 }
560
561 Frame
562 Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
563 {
564         DCPTime s = t - piece->content->position ();
565         s = min (piece->content->length_after_trim(), s);
566         s = max (DCPTime(), s + DCPTime (piece->content->trim_start(), piece->frc));
567
568         /* It might seem more logical here to convert s to a ContentTime (using the FrameRateChange)
569            then convert that ContentTime to frames at the content's rate.  However this fails for
570            situations like content at 29.9978733fps, DCP at 30fps.  The accuracy of the Time type is not
571            enough to distinguish between the two with low values of time (e.g. 3200 in Time units).
572
573            Instead we convert the DCPTime using the DCP video rate then account for any skip/repeat.
574         */
575         return s.frames_floor (piece->frc.dcp) / piece->frc.factor ();
576 }
577
578 DCPTime
579 Player::content_video_to_dcp (shared_ptr<const Piece> piece, Frame f) const
580 {
581         /* See comment in dcp_to_content_video */
582         DCPTime const d = DCPTime::from_frames (f * piece->frc.factor(), piece->frc.dcp) - DCPTime (piece->content->trim_start (), piece->frc);
583         return max (DCPTime (), d + piece->content->position ());
584 }
585
586 Frame
587 Player::dcp_to_resampled_audio (shared_ptr<const Piece> piece, DCPTime t) const
588 {
589         DCPTime s = t - piece->content->position ();
590         s = min (piece->content->length_after_trim(), s);
591         /* See notes in dcp_to_content_video */
592         return max (DCPTime (), DCPTime (piece->content->trim_start (), piece->frc) + s).frames_floor (_film->audio_frame_rate ());
593 }
594
595 ContentTime
596 Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
597 {
598         DCPTime s = t - piece->content->position ();
599         s = min (piece->content->length_after_trim(), s);
600         return max (ContentTime (), ContentTime (s, piece->frc) + piece->content->trim_start());
601 }
602
603 DCPTime
604 Player::content_subtitle_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
605 {
606         return max (DCPTime (), DCPTime (t - piece->content->trim_start(), piece->frc) + piece->content->position());
607 }
608
609 /** @param burnt true to return only subtitles to be burnt, false to return only
610  *  subtitles that should not be burnt.  This parameter will be ignored if
611  *  _always_burn_subtitles is true; in this case, all subtitles will be returned.
612  */
613 PlayerSubtitles
614 Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt, bool accurate)
615 {
616         list<shared_ptr<Piece> > subs = overlaps (time, time + length, has_subtitle);
617
618         PlayerSubtitles ps (time, length);
619
620         for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
621                 if (!(*j)->content->subtitle->use () || (!_always_burn_subtitles && (burnt != (*j)->content->subtitle->burn ()))) {
622                         continue;
623                 }
624
625                 shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> ((*j)->content);
626                 if (dcp_content && dcp_content->reference_subtitle () && !_play_referenced) {
627                         continue;
628                 }
629
630                 shared_ptr<SubtitleDecoder> subtitle_decoder = (*j)->decoder->subtitle;
631                 ContentTime const from = dcp_to_content_subtitle (*j, time);
632                 /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
633                 ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
634
635                 list<ContentImageSubtitle> image = subtitle_decoder->get_image (ContentTimePeriod (from, to), starting, accurate);
636                 for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
637
638                         /* Apply content's subtitle offsets */
639                         i->sub.rectangle.x += (*j)->content->subtitle->x_offset ();
640                         i->sub.rectangle.y += (*j)->content->subtitle->y_offset ();
641
642                         /* Apply content's subtitle scale */
643                         i->sub.rectangle.width *= (*j)->content->subtitle->x_scale ();
644                         i->sub.rectangle.height *= (*j)->content->subtitle->y_scale ();
645
646                         /* Apply a corrective translation to keep the subtitle centred after that scale */
647                         i->sub.rectangle.x -= i->sub.rectangle.width * ((*j)->content->subtitle->x_scale() - 1);
648                         i->sub.rectangle.y -= i->sub.rectangle.height * ((*j)->content->subtitle->y_scale() - 1);
649
650                         ps.image.push_back (i->sub);
651                 }
652
653                 list<ContentTextSubtitle> text = subtitle_decoder->get_text (ContentTimePeriod (from, to), starting, accurate);
654                 BOOST_FOREACH (ContentTextSubtitle& ts, text) {
655                         BOOST_FOREACH (dcp::SubtitleString s, ts.subs) {
656                                 s.set_h_position (s.h_position() + (*j)->content->subtitle->x_offset ());
657                                 s.set_v_position (s.v_position() + (*j)->content->subtitle->y_offset ());
658                                 float const xs = (*j)->content->subtitle->x_scale();
659                                 float const ys = (*j)->content->subtitle->y_scale();
660                                 float size = s.size();
661
662                                 /* Adjust size to express the common part of the scaling;
663                                    e.g. if xs = ys = 0.5 we scale size by 2.
664                                 */
665                                 if (xs > 1e-5 && ys > 1e-5) {
666                                         size *= 1 / min (1 / xs, 1 / ys);
667                                 }
668                                 s.set_size (size);
669
670                                 /* Then express aspect ratio changes */
671                                 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
672                                         s.set_aspect_adjust (xs / ys);
673                                 }
674                                 s.set_in (dcp::Time(content_subtitle_to_dcp (*j, ts.period().from).seconds(), 1000));
675                                 s.set_out (dcp::Time(content_subtitle_to_dcp (*j, ts.period().to).seconds(), 1000));
676                                 ps.text.push_back (s);
677                                 ps.add_fonts ((*j)->content->subtitle->fonts ());
678                         }
679                 }
680         }
681
682         return ps;
683 }
684
685 list<shared_ptr<Font> >
686 Player::get_subtitle_fonts ()
687 {
688         if (!_have_valid_pieces) {
689                 setup_pieces ();
690         }
691
692         list<shared_ptr<Font> > fonts;
693         BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
694                 if (p->content->subtitle) {
695                         /* XXX: things may go wrong if there are duplicate font IDs
696                            with different font files.
697                         */
698                         list<shared_ptr<Font> > f = p->content->subtitle->fonts ();
699                         copy (f.begin(), f.end(), back_inserter (fonts));
700                 }
701         }
702
703         return fonts;
704 }
705
706 /** Set this player never to produce any video data */
707 void
708 Player::set_ignore_video ()
709 {
710         _ignore_video = true;
711 }
712
713 /** Set this player never to produce any audio data */
714 void
715 Player::set_ignore_audio ()
716 {
717         _ignore_audio = true;
718 }
719
720 /** Set whether or not this player should always burn text subtitles into the image,
721  *  regardless of the content settings.
722  *  @param burn true to always burn subtitles, false to obey content settings.
723  */
724 void
725 Player::set_always_burn_subtitles (bool burn)
726 {
727         _always_burn_subtitles = burn;
728 }
729
730 void
731 Player::set_fast ()
732 {
733         _fast = true;
734         _have_valid_pieces = false;
735 }
736
737 void
738 Player::set_play_referenced ()
739 {
740         _play_referenced = true;
741         _have_valid_pieces = false;
742 }
743
744 list<ReferencedReelAsset>
745 Player::get_reel_assets ()
746 {
747         list<ReferencedReelAsset> a;
748
749         BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
750                 shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i);
751                 if (!j) {
752                         continue;
753                 }
754
755                 scoped_ptr<DCPDecoder> decoder;
756                 try {
757                         decoder.reset (new DCPDecoder (j, _film->log(), false));
758                 } catch (...) {
759                         return a;
760                 }
761
762                 int64_t offset = 0;
763                 BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
764                         DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
765                         if (j->reference_video ()) {
766                                 a.push_back (
767                                         ReferencedReelAsset (
768                                                 k->main_picture (),
769                                                 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_picture()->duration(), _film->video_frame_rate()))
770                                                 )
771                                         );
772                         }
773
774                         if (j->reference_audio ()) {
775                                 a.push_back (
776                                         ReferencedReelAsset (
777                                                 k->main_sound (),
778                                                 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_sound()->duration(), _film->video_frame_rate()))
779                                                 )
780                                         );
781                         }
782
783                         if (j->reference_subtitle ()) {
784                                 DCPOMATIC_ASSERT (k->main_subtitle ());
785                                 a.push_back (
786                                         ReferencedReelAsset (
787                                                 k->main_subtitle (),
788                                                 DCPTimePeriod (from, from + DCPTime::from_frames (k->main_subtitle()->duration(), _film->video_frame_rate()))
789                                                 )
790                                         );
791                         }
792
793                         /* Assume that main picture duration is the length of the reel */
794                         offset += k->main_picture()->duration ();
795                 }
796         }
797
798         return a;
799 }
800
801 list<shared_ptr<Piece> >
802 Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid)
803 {
804         if (!_have_valid_pieces) {
805                 setup_pieces ();
806         }
807
808         list<shared_ptr<Piece> > overlaps;
809         BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
810                 if (valid (i->content.get ()) && i->content->position() < to && i->content->end() > from) {
811                         overlaps.push_back (i);
812                 }
813         }
814
815         return overlaps;
816 }