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