+ if (i->done) {
+ continue;
+ }
+
+ DCPTime const t = content_time_to_dcp (i, max(i->decoder->position(), i->content->trim_start()));
+ if (t > i->content->end(_film)) {
+ i->done = true;
+ } else {
+
+ /* Given two choices at the same time, pick the one with texts so we see it before
+ the video.
+ */
+ if (!earliest_time || t < *earliest_time || (t == *earliest_time && !i->decoder->text.empty())) {
+ earliest_time = t;
+ earliest_content = i;
+ }
+ }
+ }
+
+ bool done = false;
+
+ enum {
+ NONE,
+ CONTENT,
+ BLACK,
+ SILENT
+ } which = NONE;
+
+ if (earliest_content) {
+ which = CONTENT;
+ }
+
+ if (!_black.done() && (!earliest_time || _black.position() < *earliest_time)) {
+ earliest_time = _black.position ();
+ which = BLACK;
+ }
+
+ if (!_silent.done() && (!earliest_time || _silent.position() < *earliest_time)) {
+ earliest_time = _silent.position ();
+ which = SILENT;
+ }
+
+ switch (which) {
+ case CONTENT:
+ {
+ earliest_content->done = earliest_content->decoder->pass ();
+ shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent>(earliest_content->content);
+ if (dcp && !_play_referenced && dcp->reference_audio()) {
+ /* We are skipping some referenced DCP audio content, so we need to update _last_audio_time
+ to `hide' the fact that no audio was emitted during the referenced DCP (though
+ we need to behave as though it was).
+ */
+ _last_audio_time = dcp->end (_film);
+ }
+ break;
+ }
+ case BLACK:
+ emit_video (black_player_video_frame(EYES_BOTH), _black.position());
+ _black.set_position (_black.position() + one_video_frame());
+ break;
+ case SILENT:
+ {
+ DCPTimePeriod period (_silent.period_at_position());
+ if (_last_audio_time) {
+ /* Sometimes the thing that happened last finishes fractionally before
+ or after this silence. Bodge the start time of the silence to fix it.
+ I think this is nothing to worry about since we will just add or
+ remove a little silence at the end of some content.
+ */
+ int64_t const error = labs(period.from.get() - _last_audio_time->get());
+ int64_t const too_much_error = 4;
+ if (error >= too_much_error) {
+ _film->log()->log(String::compose("Silence starting before or after last audio by %1", error), LogEntry::TYPE_ERROR);
+ }
+ DCPOMATIC_ASSERT (error < too_much_error);
+ period.from = *_last_audio_time;
+ }
+ if (period.duration() > one_video_frame()) {
+ period.to = period.from + one_video_frame();
+ }
+ fill_audio (period);
+ _silent.set_position (period.to);
+ break;
+ }
+ case NONE:
+ done = true;
+ break;
+ }
+
+ /* Emit any audio that is ready */
+
+ /* Work out the time before which the audio is definitely all here. This is the earliest last_push_end of one
+ of our streams, or the position of the _silent.
+ */
+ DCPTime pull_to = _film->length ();
+ for (map<AudioStreamPtr, StreamState>::const_iterator i = _stream_states.begin(); i != _stream_states.end(); ++i) {
+ if (!i->second.piece->done && i->second.last_push_end < pull_to) {
+ pull_to = i->second.last_push_end;
+ }
+ }
+ if (!_silent.done() && _silent.position() < pull_to) {
+ pull_to = _silent.position();
+ }
+
+ list<pair<shared_ptr<AudioBuffers>, DCPTime> > audio = _audio_merger.pull (pull_to);
+ for (list<pair<shared_ptr<AudioBuffers>, DCPTime> >::iterator i = audio.begin(); i != audio.end(); ++i) {
+ if (_last_audio_time && i->second < *_last_audio_time) {
+ /* This new data comes before the last we emitted (or the last seek); discard it */
+ pair<shared_ptr<AudioBuffers>, DCPTime> cut = discard_audio (i->first, i->second, *_last_audio_time);
+ if (!cut.first) {
+ continue;
+ }
+ *i = cut;
+ } else if (_last_audio_time && i->second > *_last_audio_time) {
+ /* There's a gap between this data and the last we emitted; fill with silence */
+ fill_audio (DCPTimePeriod (*_last_audio_time, i->second));
+ }
+
+ emit_audio (i->first, i->second);
+ }
+
+ if (done) {
+ _shuffler->flush ();
+ for (list<pair<shared_ptr<PlayerVideo>, DCPTime> >::const_iterator i = _delay.begin(); i != _delay.end(); ++i) {
+ do_emit_video(i->first, i->second);