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