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