google/ExoPlayer · error · VpxDecoderException

Vpx decoder does not support secure decode.

Error message

Vpx decoder does not support secure decode.

What it means

The VpxDecoder constructor rejects playback when a non-null CryptoConfig is supplied (the stream is DRM-protected) but the loaded libvpx build does not advertise secure decode support (VpxLibrary.vpxIsSecureDecodeSupported() == false). Secure decode requires a specially built libvpx with protected-path support.

Source

Thrown at extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxDecoder.java:84

   * @param cryptoConfig The {@link CryptoConfig} object required for decoding encrypted content.
   *     May be null and can be ignored if decoder does not handle encrypted content.
   * @param threads Number of threads libvpx will use to decode.
   * @throws VpxDecoderException Thrown if an exception occurs when initializing the decoder.
   */
  public VpxDecoder(
      int numInputBuffers,
      int numOutputBuffers,
      int initialInputBufferSize,
      @Nullable CryptoConfig cryptoConfig,
      int threads)
      throws VpxDecoderException {
    super(new DecoderInputBuffer[numInputBuffers], new VideoDecoderOutputBuffer[numOutputBuffers]);
    if (!VpxLibrary.isAvailable()) {
      throw new VpxDecoderException("Failed to load decoder native libraries.");
    }
    this.cryptoConfig = cryptoConfig;
    if (cryptoConfig != null && !VpxLibrary.vpxIsSecureDecodeSupported()) {
      throw new VpxDecoderException("Vpx decoder does not support secure decode.");
    }
    vpxDecContext =
        vpxInit(/* disableLoopFilter= */ false, /* enableRowMultiThreadMode= */ false, threads);
    if (vpxDecContext == 0) {
      throw new VpxDecoderException("Failed to initialize decoder");
    }
    setInitialInputBufferSize(initialInputBufferSize);
  }

  @Override
  public String getName() {
    return "libvpx" + VpxLibrary.getVersion();
  }

  @Override
  protected DecoderInputBuffer createInputBuffer() {
    return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
  }

View on GitHub (pinned to dd430f7053)

Solutions

  1. For DRM VP9 content, prefer the platform/MediaCodec secure decoder (setSecureDecoder(true) path) instead of the libvpx extension.
  2. If you truly need libvpx secure decode, rebuild the extension's native code with secure-decode support so vpxIsSecureDecodeSupported() returns true.
  3. At track selection, exclude the libvpx renderer for DRM-protected content by inspecting whether the format requires crypto.
  4. As a last resort allow non-secure playback only if the license permits it (clear-content fallback).

Example fix

// before
// DRM VP9 session routed to LibvpxVideoRenderer -> throws in VpxDecoder ctor

// after
RenderersFactory factory = new DefaultRenderersFactory(context) {
  @Override protected void buildVideoRenderers(...) {
    // skip LibvpxVideoRenderer when the content uses DRM
    if (drmScheme == null) super.buildVideoRenderers(...);
  }
};
Defensive patterns

Strategy: validation

Validate before calling

boolean secureVpx = VpxLibrary.isAvailable() && VpxLibrary.vpxIsSecureDecodeSupported();
if (drmContent && !secureVpx) { /* exclude libvpx renderer for this track */ }

Prevention

When it happens

Trigger: DrmSessionManager provides a CryptoConfig for the VP9 track (e.g. Widevine L1 secure decoder required) while the extension's libvpx was built without secure decode. Combination of DRM content plus the software VP9 extension.

Common situations: Playing Widevine-protected VP9 through the libvpx extension on a stock extension build; devices where the hardware secure path is unavailable so ExoPlayer falls back to the extension renderer with a CryptoConfig.

Related errors


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