google/ExoPlayer · critical · RuntimeException

Error instantiating Opus extension

Error message

Error instantiating Opus extension

What it means

DefaultRenderersFactory reflectively instantiates the optional Opus decoder renderer (com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer) using its (Handler, AudioRendererEventListener, AudioSink) constructor. ClassNotFoundException means the extension was not packaged and is fine; any other reflective failure means the extension is present but broken, so a RuntimeException wrapping the cause is thrown to abort player construction rather than proceed with a half-working extension.

Source

Thrown at library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java:507

    }

    try {
      // Full class names used for constructor args so the LINT rule triggers if any of them move.
      Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
      Constructor<?> constructor =
          clazz.getConstructor(
              android.os.Handler.class,
              com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
              com.google.android.exoplayer2.audio.AudioSink.class);
      Renderer renderer =
          (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
      out.add(extensionRendererIndex++, renderer);
      Log.i(TAG, "Loaded LibopusAudioRenderer.");
    } catch (ClassNotFoundException e) {
      // Expected if the app was built without the extension.
    } catch (Exception e) {
      // The extension is present, but instantiation failed.
      throw new RuntimeException("Error instantiating Opus extension", e);
    }

    try {
      // Full class names used for constructor args so the LINT rule triggers if any of them move.
      Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
      Constructor<?> constructor =
          clazz.getConstructor(
              android.os.Handler.class,
              com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
              com.google.android.exoplayer2.audio.AudioSink.class);
      Renderer renderer =
          (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
      out.add(extensionRendererIndex++, renderer);
      Log.i(TAG, "Loaded LibflacAudioRenderer.");
    } catch (ClassNotFoundException e) {
      // Expected if the app was built without the extension.
    } catch (Exception e) {
      // The extension is present, but instantiation failed.

View on GitHub (pinned to dd430f7053)

Solutions

  1. Unwrap RuntimeException.getCause() to distinguish NoSuchMethodException (signature drift) from UnsatisfiedLinkError (missing native lib).
  2. Align exoplayer-opus and exoplayer-core to the exact same version.
  3. Add keep rules for com.google.android.exoplayer2.ext.opus.** including constructors.
  4. Package libopus for all supported ABIs or switch to EXTENSION_RENDERER_MODE_OFF and rely on MediaCodec Opus decoders (Android 10+).

Example fix

// before
new ExoPlayer.Builder(
    new DefaultRenderersFactory(ctx)
        .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER))
    .build(ctx);

// after — construct the Opus renderer directly from the extension artifact
// (no reflection, fails at compile time if APIs drift)
RenderersFactory factory =
    new DefaultRenderersFactory(ctx) {
      @Override protected void buildAudioRenderers(...) {
        out.add(index++, new LibopusAudioRenderer(eventHandler, eventListener, audioSink));
        super.buildAudioRenderers(...);
      }
    };
Defensive patterns

Strategy: try-catch

Validate before calling

boolean opusExtensionOk = false;
try {
  Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer")
      .getConstructor(Handler.class, AudioRendererEventListener.class, AudioSink.class);
  opusExtensionOk = true;
} catch (ReflectiveOperationException ignored) {
}
// prefer platform Opus (Android 10+) unless software decode needed

Try / catch

try {
  return new ExoPlayer.Builder(opusFactory).build(context);
} catch (RuntimeException e) {
  logExtensionFailure("opus", e.getCause());
  return new ExoPlayer.Builder(new DefaultRenderersFactory(context)).build(context);
}

Prevention

When it happens

Trigger: Extension renderer mode ON/PREFER with the exoplayer-opus artifact on the classpath whose constructor signature does not match, is obfuscated away, or throws during instantiation (native libopus .so load failure).

Common situations: Version drift between exoplayer-core and exoplayer-opus; release builds with aggressive shrinking that renames Handler/AudioSink parameter types referenced by string in getConstructor; missing libopus .so for armeabi-v7a/arm64-v8a/x86 splits.

Related errors


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