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