+ return {};
+ }
+
+ return *stream;
+}
+
+
+void
+FFmpegDecoder::process_audio_frame (shared_ptr<FFmpegAudioStream> stream)
+{
+ auto data = deinterleave_audio (stream);
+
+ ContentTime ct;
+ if (_frame->pts == AV_NOPTS_VALUE) {
+ /* In some streams we see not every frame coming through with a timestamp; for those
+ that have AV_NOPTS_VALUE we need to work out the timestamp ourselves. This is
+ particularly noticeable with TrueHD streams (see #1111).
+ */
+ if (_next_time[stream]) {
+ ct = *_next_time[stream];
+ }
+ } else {
+ ct = ContentTime::from_seconds (
+ _frame->best_effort_timestamp *
+ av_q2d (stream->stream(_format_context)->time_base))
+ + _pts_offset;
+ }
+
+ _next_time[stream] = ct + ContentTime::from_frames(data->frames(), stream->frame_rate());
+
+ if (ct < ContentTime()) {
+ /* Discard audio data that comes before time 0 */
+ auto const remove = min (int64_t(data->frames()), (-ct).frames_ceil(double(stream->frame_rate())));
+ data->move (data->frames() - remove, remove, 0);
+ data->set_frames (data->frames() - remove);
+ ct += ContentTime::from_frames (remove, stream->frame_rate());
+ }
+
+ if (ct < ContentTime()) {
+ LOG_WARNING (
+ "Crazy timestamp %1 for %2 samples in stream %3 (ts=%4 tb=%5, off=%6)",
+ to_string(ct),
+ data->frames(),
+ stream->id(),
+ _frame->best_effort_timestamp,
+ av_q2d(stream->stream(_format_context)->time_base),
+ to_string(_pts_offset)
+ );
+ }
+
+ /* Give this data provided there is some, and its time is sane */
+ if (ct >= ContentTime() && data->frames() > 0) {
+ audio->emit (film(), stream, data, ct);
+ }
+}
+
+
+pair<int, bool>
+FFmpegDecoder::decode_audio_packet (shared_ptr<FFmpegAudioStream> stream, AVPacket* packet)
+{
+ int frame_finished;
+ DCPOMATIC_DISABLE_WARNINGS
+ int decode_result = avcodec_decode_audio4 (stream->stream(_format_context)->codec, _frame, &frame_finished, packet);
+ DCPOMATIC_ENABLE_WARNINGS
+ if (decode_result < 0) {
+ /* avcodec_decode_audio4 can sometimes return an error even though it has decoded
+ some valid data; for example dca_subframe_footer can return AVERROR_INVALIDDATA
+ if it overreads the auxiliary data. ffplay carries on if frame_finished is true,
+ even in the face of such an error, so I think we should too.
+
+ Returning from the method here caused mantis #352.
+ */
+ LOG_WARNING ("avcodec_decode_audio4 failed (%1)", decode_result);
+ }
+ return make_pair(decode_result, frame_finished);
+}
+
+
+void
+FFmpegDecoder::decode_and_process_audio_packet (AVPacket* packet)
+{
+ auto stream = audio_stream_from_index (packet->stream_index);
+ if (!stream) {