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