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