google/ExoPlayer · error · OpusDecoderException

Invalid channel count: ${channelCount}

Error message

Invalid channel count: ${channelCount}

What it means

The Opus identification header declares a channel count greater than 8, which the ExoPlayer Opus extension refuses to initialize. libopus multistream is capped here at 8 channels; anything higher indicates a corrupt or non-Opus header byte being read at offset 9.

Source

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

    int initializationDataSize = initializationData.size();
    if (initializationDataSize != 1 && initializationDataSize != 3) {
      throw new OpusDecoderException("Invalid initialization data size");
    }
    if (initializationDataSize == 3
        && (initializationData.get(1).length != 8 || initializationData.get(2).length != 8)) {
      throw new OpusDecoderException("Invalid pre-skip or seek pre-roll");
    }
    preSkipSamples = getPreSkipSamples(initializationData);
    seekPreRollSamples = getSeekPreRollSamples(initializationData);
    skipSamples = preSkipSamples;

    byte[] headerBytes = initializationData.get(0);
    if (headerBytes.length < 19) {
      throw new OpusDecoderException("Invalid header length");
    }
    channelCount = getChannelCount(headerBytes);
    if (channelCount > 8) {
      throw new OpusDecoderException("Invalid channel count: " + channelCount);
    }
    int gain = readSignedLittleEndian16(headerBytes, 16);

    byte[] streamMap = new byte[8];
    int numStreams;
    int numCoupled;
    if (headerBytes[18] == 0) { // Channel mapping
      // If there is no channel mapping, use the defaults.
      if (channelCount > 2) { // Maximum channel count with default layout.
        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");

View on GitHub (pinned to dd430f7053)

Solutions

  1. Inspect initializationData[0]: it must start with 'OpusHead' and byte 9 must be 1-8; fix the producer of that header if not.
  2. Confirm the track's MIME type is audio/opus so non-Opus streams never reach OpusDecoder.
  3. Re-transcode or re-mux the source file if the header in the container itself is corrupt.
  4. Exclude the Opus extension renderer via RenderersFactory filtering if you only meant to play standard MediaCodec audio.

Example fix

// before
// format passed straight from extractor to Opus renderer, header byte 9 = 200

// after
byte[] h = format.initializationData.get(0);
int channels = h[9] & 0xFF;
if (channels < 1 || channels > 8) {
  // skip / deselect this track instead of letting the decoder throw
  selectionParameters.overrideExclude(audioTrackGroup); 
}
Defensive patterns

Strategy: validation

Validate before calling

int channels = h[9] & 0xFF;
if (channels < 1 || channels > 8) { /* reject track before renderer init */ }

Prevention

When it happens

Trigger: getChannelCount(headerBytes) reads the unsigned byte at offset 9 of initializationData[0] and it is > 8. Caused by files whose header is actually not an Opus ID header (wrong initializationData), corrupted headers, or hand-built initializationData with the wrong layout.

Common situations: Misrouted audio track (e.g. a different codec's CodecPrivate passed as Opus header); corrupted downloads or malformed transcodes producing a garbage channel byte; incorrect manual Format construction in IMA/media-source code.

Related errors


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