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