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