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