google/ExoPlayer · error · MediaCodecRenderer.DecoderInitializationException

NO_SUITABLE_DECODER_ERROR

NO_SUITABLE_DECODER_ERROR

Error message

Decoder init failed: [{errorCode}], {format}

What it means

After the codec query succeeds, MediaCodecRenderer checks the deque of candidate MediaCodecInfos; if it is empty, no installed decoder matches the input format (considering the secure-decoder requirement when DRM needs secure decoding), and it throws DecoderInitializationException with NO_SUITABLE_DECODER_ERROR. Unlike DECODER_QUERY_ERROR, the codec database was queried fine — the device simply has no decoder for the format/profile.

Source

Thrown at library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java:1027

            getAvailableCodecInfos(mediaCryptoRequiresSecureDecoder);
        availableCodecInfos = new ArrayDeque<>();
        if (enableDecoderFallback) {
          availableCodecInfos.addAll(allAvailableCodecInfos);
        } else if (!allAvailableCodecInfos.isEmpty()) {
          availableCodecInfos.add(allAvailableCodecInfos.get(0));
        }
        preferredDecoderInitializationException = null;
      } catch (DecoderQueryException e) {
        throw new DecoderInitializationException(
            inputFormat,
            e,
            mediaCryptoRequiresSecureDecoder,
            DecoderInitializationException.DECODER_QUERY_ERROR);
      }
    }

    if (availableCodecInfos.isEmpty()) {
      throw new DecoderInitializationException(
          inputFormat,
          /* cause= */ null,
          mediaCryptoRequiresSecureDecoder,
          DecoderInitializationException.NO_SUITABLE_DECODER_ERROR);
    }

    MediaCodecInfo preferredCodecInfo = availableCodecInfos.peekFirst();
    while (codec == null) {
      MediaCodecInfo codecInfo = availableCodecInfos.peekFirst();
      if (!shouldInitCodec(codecInfo)) {
        return;
      }
      try {
        try {
          initCodec(codecInfo, crypto);
        } catch (Exception e) {
          if (codecInfo == preferredCodecInfo) {
            // If creating the preferred decoder failed then sleep briefly before retrying.

View on GitHub (pinned to dd430f7053)

Solutions

  1. Offer multiple renditions/variants (H.264 baseline/main fallback alongside HEVC/AV1) and let ABR + track selection pick a supported codec per device.
  2. Before playback, query support with MediaCodecUtil.getCodecInfos / MediaCapability and skip or replace unsupported tracks (e.g. prefer an audio-only or lower-profile variant).
  3. For niche codecs, ship the FFmpeg extension (software video/audio renderers) so decoding is not dependent on OEM hardware.
  4. For DRM secure-decoder failures, check MediaCodecInfo.hardwareAccelerated/secure playback support per codec and consider L3 streams for devices lacking a secure decoder for that MIME type.

Example fix

// before
TrackSelectionParameters params = player.getTrackSelectionParameters(); // defaults: unsupported video may still be selected first try

// after
TrackSelectionParameters params = player.getTrackSelectionParameters()
    .buildUpon()
    .setVideoResolutionCeilingForSizeConstrained(1920)
    // prefer codecs known broadly supported when packaging offers them
    .setOverrideForType(new TrackSelectionOverride(
        new TrackGroupInfo..., /* H.264 track index */ 0))
    .build();
player.setTrackSelectionParameters(params);
Defensive patterns

Strategy: validation

Validate before calling

// Before playback, check the device can decode the primary codec:
MimeTypes primary = MimeTypes.VIDEO_H265;
boolean hevcSupported = false;
try {
  for (MediaCodecInfo info : MediaCodecUtil.getCodecInfos()) {
    if (info.isDecoder() && info.supportedMimeTypes.contains(primary)
        && info.getCapabilitiesForType(primary).isFormatSupported(format)) {
      hevcSupported = true;
      break;
    }
  }
} catch (DecoderQueryException e) {
  hevcSupported = false;
}
if (!hevcSupported) {
  // select H.264 variant / lower profile instead of failing with NO_SUITABLE_DECODER
}

Try / catch

@Override
public void onPlayerError(PlaybackException error) {
  if (error.getCause() instanceof DecoderInitializationException
      && ((DecoderInitializationException) error.getCause()).getDecoderName() == null) {
    DecoderInitializationException die = (DecoderInitializationException) error.getCause();
    if (die.getCause() == null /* no cause -> no suitable decoder */) {
      // codec unsupported: switch to fallback rendition (H.264) or audio-only
    }
  }
}

Prevention

When it happens

Trigger: Stream codec/profile not supported by the hardware: HEVC on devices without an HEVC decoder, AV1 on pre-Android-10/pre-AV1 devices, H.264 High-10 profile, Dolby Vision, DTS:X etc.; or mediaCryptoRequiresSecureDecoder=true (DRM L1 HD content) and the only matching decoder is insecure, filtering the deque to empty.

Common situations: Single-rendition streams assuming universal codec support; HLS/DASH packaging without per-device variant selection; secure-path DRM content on devices that only offer insecure decoders for that codec; emulators lacking hardware codecs; 10-bit HDR profiles on older chipsets.

Related errors


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