From 14027bc98e1c569d889361d14586edfae087a162 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 4 Jan 2019 15:07:13 +0000 Subject: [PATCH] Treat AVERROR_INVALIDDATA as non-fatal Also configure the FFmpeg context to ignore errors as far as possible (this appears to have an effect only for certain decoders). Issue: #5293 PiperOrigin-RevId: 227851397 --- RELEASENOTES.md | 2 ++ .../exoplayer2/ext/ffmpeg/FfmpegDecoder.java | 14 ++++++++++++-- extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc | 10 ++++++++-- .../audio/SimpleDecoderAudioRenderer.java | 5 ++++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d0eb94d00a6..71affdd4207 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -33,6 +33,8 @@ * Clear ads loader listeners on release ([#4114](https://github.com/google/ExoPlayer/issues/4114)). * Require setting the `Player` on `AdsLoader` instances before playback. +* FFmpeg extension: Treat invalid data errors as non-fatal to match the behavior + of MediaCodec ([#5293](https://github.com/google/ExoPlayer/issues/5293)). * Fix issue where sending callbacks for playlist changes may cause problems because of parallel player access ([#5240](https://github.com/google/ExoPlayer/issues/5240)). diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java index 6f3c623f3fa..c5b76002fa9 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java @@ -37,6 +37,10 @@ private static final int OUTPUT_BUFFER_SIZE_16BIT = 65536; private static final int OUTPUT_BUFFER_SIZE_32BIT = OUTPUT_BUFFER_SIZE_16BIT * 2; + // Error codes matching ffmpeg_jni.cc. + private static final int DECODER_ERROR_INVALID_DATA = -1; + private static final int DECODER_ERROR_OTHER = -2; + private final String codecName; private final @Nullable byte[] extraData; private final @C.Encoding int encoding; @@ -106,8 +110,14 @@ protected FfmpegDecoderException createUnexpectedDecodeException(Throwable error int inputSize = inputData.limit(); ByteBuffer outputData = outputBuffer.init(inputBuffer.timeUs, outputBufferSize); int result = ffmpegDecode(nativeContext, inputData, inputSize, outputData, outputBufferSize); - if (result < 0) { - return new FfmpegDecoderException("Error decoding (see logcat). Code: " + result); + if (result == DECODER_ERROR_INVALID_DATA) { + // Treat invalid data errors as non-fatal to match the behavior of MediaCodec. No output will + // be produced for this buffer, so mark it as decode-only to ensure that the audio sink's + // position is reset when more audio is produced. + outputBuffer.setFlags(C.BUFFER_FLAG_DECODE_ONLY); + return null; + } else if (result == DECODER_ERROR_OTHER) { + return new FfmpegDecoderException("Error decoding (see logcat)."); } if (!hasOutputFormat) { channelCount = ffmpegGetChannelCount(nativeContext); diff --git a/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc b/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc index 87579ebb9a4..dcd4560e4ab 100644 --- a/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc +++ b/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc @@ -63,6 +63,10 @@ static const AVSampleFormat OUTPUT_FORMAT_PCM_16BIT = AV_SAMPLE_FMT_S16; // Output format corresponding to AudioFormat.ENCODING_PCM_FLOAT. static const AVSampleFormat OUTPUT_FORMAT_PCM_FLOAT = AV_SAMPLE_FMT_FLT; +// Error codes matching FfmpegDecoder.java. +static const int DECODER_ERROR_INVALID_DATA = -1; +static const int DECODER_ERROR_OTHER = -2; + /** * Returns the AVCodec with the specified name, or NULL if it is not available. */ @@ -79,7 +83,7 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData, /** * Decodes the packet into the output buffer, returning the number of bytes - * written, or a negative value in the case of an error. + * written, or a negative DECODER_ERROR constant value in the case of an error. */ int decodePacket(AVCodecContext *context, AVPacket *packet, uint8_t *outputBuffer, int outputSize); @@ -238,6 +242,7 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData, context->channels = rawChannelCount; context->channel_layout = av_get_default_channel_layout(rawChannelCount); } + context->err_recognition = AV_EF_IGNORE_ERR; int result = avcodec_open2(context, codec, NULL); if (result < 0) { logError("avcodec_open2", result); @@ -254,7 +259,8 @@ int decodePacket(AVCodecContext *context, AVPacket *packet, result = avcodec_send_packet(context, packet); if (result) { logError("avcodec_send_packet", result); - return result; + return result == AVERROR_INVALIDDATA ? DECODER_ERROR_INVALID_DATA + : DECODER_ERROR_OTHER; } // Dequeue output data until it runs out. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java index 287cae9d413..bfd7bbc1653 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java @@ -366,7 +366,10 @@ private boolean drainOutputBuffer() throws ExoPlaybackException, AudioDecoderExc if (outputBuffer == null) { return false; } - decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount; + if (outputBuffer.skippedOutputBufferCount > 0) { + decoderCounters.skippedOutputBufferCount += outputBuffer.skippedOutputBufferCount; + audioSink.handleDiscontinuity(); + } } if (outputBuffer.isEndOfStream()) {