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