google/ExoPlayer · error · OpusDecoderException

Failed to initialize decoder

Error message

Failed to initialize decoder

What it means

The native opusInit(...) call returned 0 (a null context), so the JNI multistream decoder could not be created from the supplied channel count, stream count, coupled count, gain and stream map. It means the header passed structural validation but libopus rejected the configuration (e.g. numStreams/numCoupled inconsistent with channelCount, or the native library failed internally).

Source

Thrown at extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java:145

        throw new OpusDecoderException("Invalid header, missing stream map");
      }
      numStreams = 1;
      numCoupled = (channelCount == 2) ? 1 : 0;
      streamMap[0] = 0;
      streamMap[1] = 1;
    } else {
      if (headerBytes.length < 21 + channelCount) {
        throw new OpusDecoderException("Invalid header length");
      }
      // Read the channel mapping.
      numStreams = headerBytes[19] & 0xFF;
      numCoupled = headerBytes[20] & 0xFF;
      System.arraycopy(headerBytes, 21, streamMap, 0, channelCount);
    }
    nativeDecoderContext =
        opusInit(SAMPLE_RATE, channelCount, numStreams, numCoupled, gain, streamMap);
    if (nativeDecoderContext == 0) {
      throw new OpusDecoderException("Failed to initialize decoder");
    }
    setInitialInputBufferSize(initialInputBufferSize);

    this.outputFloat = outputFloat;
    if (outputFloat) {
      opusSetFloatOutput();
    }
  }

  /**
   * Sets whether discard padding is enabled. When enabled, discard padding samples (provided as
   * supplemental data on the input buffer) will be removed from the end of the decoder output.
   *
   * <p>This method is experimental, and will be renamed or removed in a future release.
   */
  public void experimentalSetDiscardPaddingEnabled(boolean enabled) {
    this.experimentalDiscardPaddingEnabled = enabled;
  }

View on GitHub (pinned to dd430f7053)

Solutions

  1. Validate header consistency before playback: channelCount <= numStreams + 2*numCoupled and numStreams >= 1.
  2. Re-encode the audio with a known-good encoder (ffmpeg opus / libopus) so mapping metadata is coherent.
  3. If building the extension from source, confirm the JNI opusInit wrapper and linked libopus support multistream (OPUS_MULTISTREAM).
  4. Fall back to MediaCodec ('audio/opus' hardware/passthrough) by excluding the extension renderer.

Example fix

// before
// multichannel family-1 header with channels=6, numStreams=1, numCoupled=0 -> opusInit fails

// after
int channels = h[9] & 0xFF, ns = h[19] & 0xFF, nc = h[20] & 0xFF;
if (channels > ns + 2 * nc) {
  // header is inconsistent; deselect the track or fail with a clear message
  throw new IOException("Opus stream counts do not cover channel count");
}
Defensive patterns

Strategy: fallback

Validate before calling

int channels = h[9] & 0xFF, ns = h[19] & 0xFF, nc = h[20] & 0xFF;
boolean consistent = ns >= 1 && channels <= ns + 2 * nc;
if (!consistent) { /* deselect opus extension track */ }

Try / catch

try { renderer.init(...); } catch (OpusDecoderException e) { /* fall back to MediaCodec audio/opus decoder or skip track */ }

Prevention

When it happens

Trigger: opusInit(SAMPLE_RATE, channelCount, numStreams, numCoupled, gain, streamMap) == 0. Typical when the header's numStreams (byte 19) and numCoupled (byte 20) do not satisfy channelCount <= numStreams + numCoupled*2, or when the bundled libopus JNI wrapper is built without multistream support.

Common situations: Malformed family-1 headers where the stream counts contradict the channel count; device-specific native library issues in custom builds of the Opus extension; headers where gain is corrupt is tolerated, so almost always the stream-count mismatch.

Related errors


AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14). Data as JSON: /api/errors/ab21b0cc472db4db. Report an issue: GitHub.