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