Various fixes to push audio vaguely in the right direction.
[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                 return true;
523         }
524
525         earliest->done = earliest->decoder->pass ();
526
527         /* Emit any audio that is ready */
528
529         optional<DCPTime> earliest_audio;
530         BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
531                 if (i->decoder->audio) {
532                         DCPTime const t = i->content->position() + DCPTime (i->decoder->audio->position(), i->frc);
533                         if (!earliest_audio || t < *earliest_audio) {
534                                 earliest_audio = t;
535                         }
536                 }
537         }
538
539         pair<shared_ptr<AudioBuffers>, DCPTime> audio = _audio_merger.pull (earliest_audio.get_value_or(DCPTime()));
540         if (audio.first->frames() > 0) {
541                 DCPOMATIC_ASSERT (audio.second >= _last_audio_time);
542                 DCPTime t = _last_audio_time;
543                 while (t < audio.second) {
544                         /* Silence up to the time of this new audio */
545                         DCPTime block = min (DCPTime::from_seconds (0.5), audio.second - t);
546                         shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), block.frames_round(_film->audio_frame_rate())));
547                         silence->make_silent ();
548                         Audio (silence, t);
549                         t += block;
550                 }
551
552                 Audio (audio.first, audio.second);
553                 _last_audio_time = audio.second;
554         }
555
556         return false;
557 }
558
559 void
560 Player::video (weak_ptr<Piece> wp, ContentVideo video)
561 {
562         shared_ptr<Piece> piece = wp.lock ();
563         if (!piece) {
564                 return;
565         }
566
567         /* Time and period of the frame we will emit */
568         DCPTime const time = content_video_to_dcp (piece, video.frame.index());
569         DCPTimePeriod const period (time, time + DCPTime::from_frames (1, _film->video_frame_rate()));
570
571         /* Discard if it's outside the content's period */
572         if (time < piece->content->position() || time >= piece->content->end()) {
573                 return;
574         }
575
576         /* Get any subtitles */
577
578         optional<PositionImage> subtitles;
579
580         for (list<pair<PlayerSubtitles, DCPTimePeriod> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
581
582                 if (!i->second.overlap (period)) {
583                         continue;
584                 }
585
586                 list<PositionImage> sub_images;
587
588                 /* Image subtitles */
589                 list<PositionImage> c = transform_image_subtitles (i->first.image);
590                 copy (c.begin(), c.end(), back_inserter (sub_images));
591
592                 /* Text subtitles (rendered to an image) */
593                 if (!i->first.text.empty ()) {
594                         list<PositionImage> s = render_subtitles (i->first.text, i->first.fonts, _video_container_size, time);
595                         copy (s.begin (), s.end (), back_inserter (sub_images));
596                 }
597
598                 if (!sub_images.empty ()) {
599                         subtitles = merge (sub_images);
600                 }
601         }
602
603         /* Fill gaps */
604
605         if (_last_time) {
606                 /* XXX: this may not work for 3D */
607                 DCPTime const frame = DCPTime::from_frames (1, _film->video_frame_rate());
608                 for (DCPTime i = _last_time.get() + frame; i < time; i += frame) {
609                         if (_playlist->video_content_at(i) && _last_video) {
610                                 Video (shared_ptr<PlayerVideo> (new PlayerVideo (*_last_video)), i);
611                         } else {
612                                 Video (black_player_video_frame (), i);
613                         }
614                 }
615         }
616
617         _last_video.reset (
618                 new PlayerVideo (
619                         video.image,
620                         piece->content->video->crop (),
621                         piece->content->video->fade (video.frame.index()),
622                         piece->content->video->scale().size (
623                                 piece->content->video, _video_container_size, _film->frame_size ()
624                                 ),
625                         _video_container_size,
626                         video.frame.eyes(),
627                         video.part,
628                         piece->content->video->colour_conversion ()
629                         )
630                 );
631
632         if (subtitles) {
633                 _last_video->set_subtitle (subtitles.get ());
634         }
635
636         _last_time = time;
637
638         Video (_last_video, *_last_time);
639
640         /* Discard any subtitles we no longer need */
641
642         for (list<pair<PlayerSubtitles, DCPTimePeriod> >::iterator i = _subtitles.begin (); i != _subtitles.end(); ) {
643                 list<pair<PlayerSubtitles, DCPTimePeriod> >::iterator tmp = i;
644                 ++tmp;
645
646                 if (i->second.to < time) {
647                         _subtitles.erase (i);
648                 }
649
650                 i = tmp;
651         }
652 }
653
654 void
655 Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_audio)
656 {
657         shared_ptr<Piece> piece = wp.lock ();
658         if (!piece) {
659                 return;
660         }
661
662         shared_ptr<AudioContent> content = piece->content->audio;
663         DCPOMATIC_ASSERT (content);
664
665         /* Gain */
666         if (content->gain() != 0) {
667                 shared_ptr<AudioBuffers> gain (new AudioBuffers (content_audio.audio));
668                 gain->apply_gain (content->gain ());
669                 content_audio.audio = gain;
670         }
671
672         /* Resample */
673         if (stream->frame_rate() != content->resampled_frame_rate()) {
674                 shared_ptr<Resampler> r = resampler (content, stream, true);
675                 pair<shared_ptr<const AudioBuffers>, Frame> ro = r->run (content_audio.audio, content_audio.frame);
676                 content_audio.audio = ro.first;
677                 content_audio.frame = ro.second;
678         }
679
680         /* XXX: end-trimming used to be checked here */
681
682         /* Compute time in the DCP */
683         DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame) + DCPTime::from_seconds (content->delay() / 1000);
684
685         /* Remap channels */
686         shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), content_audio.audio->frames()));
687         dcp_mapped->make_silent ();
688
689         AudioMapping map = stream->mapping ();
690         for (int i = 0; i < map.input_channels(); ++i) {
691                 for (int j = 0; j < dcp_mapped->channels(); ++j) {
692                         if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
693                                 dcp_mapped->accumulate_channel (
694                                         content_audio.audio.get(),
695                                         i,
696                                         static_cast<dcp::Channel> (j),
697                                         map.get (i, static_cast<dcp::Channel> (j))
698                                         );
699                         }
700                 }
701         }
702
703         content_audio.audio = dcp_mapped;
704
705         if (_audio_processor) {
706                 content_audio.audio = _audio_processor->run (content_audio.audio, _film->audio_channels ());
707         }
708
709         /* XXX: this may be nonsense */
710         if (time < _audio_merger.last_pull()) {
711                 DCPTime const discard_time = _audio_merger.last_pull() - time;
712                 Frame discard_frames = discard_time.frames_round(_film->audio_frame_rate());
713                 content_audio.audio.reset (new AudioBuffers (_film->audio_channels(), content_audio.audio->frames() - discard_frames));
714                 time += discard_time;
715         }
716
717         if (content_audio.audio->frames() > 0) {
718                 _audio_merger.push (content_audio.audio, time);
719         }
720 }
721
722 void
723 Player::image_subtitle (weak_ptr<Piece> wp, ContentImageSubtitle subtitle)
724 {
725         shared_ptr<Piece> piece = wp.lock ();
726         if (!piece) {
727                 return;
728         }
729
730         /* Apply content's subtitle offsets */
731         subtitle.sub.rectangle.x += piece->content->subtitle->x_offset ();
732         subtitle.sub.rectangle.y += piece->content->subtitle->y_offset ();
733
734         /* Apply content's subtitle scale */
735         subtitle.sub.rectangle.width *= piece->content->subtitle->x_scale ();
736         subtitle.sub.rectangle.height *= piece->content->subtitle->y_scale ();
737
738         /* Apply a corrective translation to keep the subtitle centred after that scale */
739         subtitle.sub.rectangle.x -= subtitle.sub.rectangle.width * (piece->content->subtitle->x_scale() - 1);
740         subtitle.sub.rectangle.y -= subtitle.sub.rectangle.height * (piece->content->subtitle->y_scale() - 1);
741
742         PlayerSubtitles ps;
743         ps.image.push_back (subtitle.sub);
744         DCPTimePeriod period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
745
746         if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
747                 _subtitles.push_back (make_pair (ps, period));
748         } else {
749                 Subtitle (ps, period);
750         }
751 }
752
753 void
754 Player::text_subtitle (weak_ptr<Piece> wp, ContentTextSubtitle subtitle)
755 {
756         shared_ptr<Piece> piece = wp.lock ();
757         if (!piece) {
758                 return;
759         }
760
761         PlayerSubtitles ps;
762         DCPTimePeriod const period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
763
764         BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) {
765                 s.set_h_position (s.h_position() + piece->content->subtitle->x_offset ());
766                 s.set_v_position (s.v_position() + piece->content->subtitle->y_offset ());
767                 float const xs = piece->content->subtitle->x_scale();
768                 float const ys = piece->content->subtitle->y_scale();
769                 float size = s.size();
770
771                 /* Adjust size to express the common part of the scaling;
772                    e.g. if xs = ys = 0.5 we scale size by 2.
773                 */
774                 if (xs > 1e-5 && ys > 1e-5) {
775                         size *= 1 / min (1 / xs, 1 / ys);
776                 }
777                 s.set_size (size);
778
779                 /* Then express aspect ratio changes */
780                 if (fabs (1.0 - xs / ys) > dcp::ASPECT_ADJUST_EPSILON) {
781                         s.set_aspect_adjust (xs / ys);
782                 }
783
784                 s.set_in (dcp::Time(period.from.seconds(), 1000));
785                 s.set_out (dcp::Time(period.to.seconds(), 1000));
786                 ps.text.push_back (SubtitleString (s, piece->content->subtitle->outline_width()));
787                 ps.add_fonts (piece->content->subtitle->fonts ());
788         }
789
790         if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
791                 _subtitles.push_back (make_pair (ps, period));
792         } else {
793                 Subtitle (ps, period);
794         }
795 }
796
797 void
798 Player::seek (DCPTime time, bool accurate)
799 {
800         BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
801                 if (i->content->position() <= time && time < i->content->end()) {
802                         i->decoder->seek (dcp_to_content_time (i, time), accurate);
803                         i->done = false;
804                 }
805         }
806
807         if (accurate) {
808                 _last_time = time - DCPTime::from_frames (1, _film->video_frame_rate ());
809         } else {
810                 _last_time = optional<DCPTime> ();
811         }
812 }
813
814 shared_ptr<Resampler>
815 Player::resampler (shared_ptr<const AudioContent> content, AudioStreamPtr stream, bool create)
816 {
817         ResamplerMap::const_iterator i = _resamplers.find (make_pair (content, stream));
818         if (i != _resamplers.end ()) {
819                 return i->second;
820         }
821
822         if (!create) {
823                 return shared_ptr<Resampler> ();
824         }
825
826         LOG_GENERAL (
827                 "Creating new resampler from %1 to %2 with %3 channels",
828                 stream->frame_rate(),
829                 content->resampled_frame_rate(),
830                 stream->channels()
831                 );
832
833         shared_ptr<Resampler> r (
834                 new Resampler (stream->frame_rate(), content->resampled_frame_rate(), stream->channels())
835                 );
836
837         _resamplers[make_pair(content, stream)] = r;
838         return r;
839 }