google/ExoPlayer · error · Gav1DecoderException

Invalid output mode.

Error message

Invalid output mode.

What it means

Gav1DecoderException thrown by Gav1Decoder.renderToSurface when outputBuffer.mode is not C.VIDEO_OUTPUT_MODE_SURFACE_YUV. renderToSurface is only valid when the decoder was configured to render YUV frames directly onto a surface; buffers produced in buffer-output mode (or default mode 0) cannot be pushed to the surface path.

Source

Thrown at extensions/av1/src/main/java/com/google/android/exoplayer2/ext/av1/Gav1Decoder.java:177

   * @param outputMode The output mode.
   */
  public void setOutputMode(@C.VideoOutputMode int outputMode) {
    this.outputMode = outputMode;
  }

  /**
   * Renders output buffer to the given surface. Must only be called when in {@link
   * C#VIDEO_OUTPUT_MODE_SURFACE_YUV} mode.
   *
   * @param outputBuffer Output buffer.
   * @param surface Output surface.
   * @throws Gav1DecoderException Thrown if called with invalid output mode or frame rendering
   *     fails.
   */
  public void renderToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface)
      throws Gav1DecoderException {
    if (outputBuffer.mode != C.VIDEO_OUTPUT_MODE_SURFACE_YUV) {
      throw new Gav1DecoderException("Invalid output mode.");
    }
    if (gav1RenderFrame(gav1DecoderContext, surface, outputBuffer) == GAV1_ERROR) {
      throw new Gav1DecoderException(
          "Buffer render error: " + gav1GetErrorMessage(gav1DecoderContext));
    }
  }

  /**
   * Initializes a libgav1 decoder.
   *
   * @param threads Number of threads to be used by a libgav1 decoder.
   * @return The address of the decoder context or {@link #GAV1_ERROR} if there was an error.
   */
  private native long gav1Init(int threads);

  /**
   * Deallocates the decoder context.
   *

View on GitHub (pinned to dd430f7053)

Solutions

  1. Ensure setOutputMode(C.VIDEO_OUTPUT_MODE_SURFACE_YUV) is invoked on the decoder before any renderToSurface call (Libgav1VideoRenderer.setDecoderOutputMode does this, but only when decoder != null)
  2. Set the decoder output mode immediately after constructing the decoder, before processing any buffers
  3. If you need raw frames, use the buffer path (decodeToOutputBuffer) instead of the surface path

Example fix

// before
decoder.renderToSurface(outputBuffer, surface); // throws if mode never set

// after
decoder.setOutputMode(C.VIDEO_OUTPUT_MODE_SURFACE_YUV);
decoder.renderToSurface(outputBuffer, surface);
Defensive patterns

Strategy: validation

Validate before calling

if (outputBuffer.mode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV) {
  decoder.renderToSurface(outputBuffer, surface);
} else {
  // wrong path: consume the buffer via decodeToOutputBuffer() instead
  outputBuffer.release();
}

Try / catch

try {
  decoder.renderToSurface(outputBuffer, surface);
} catch (Gav1DecoderException e) {
  if (e.getMessage() != null && e.getMessage().contains("Invalid output mode")) {
    decoder.setOutputMode(C.VIDEO_OUTPUT_MODE_SURFACE_YUV); // correct state and retry once
    decoder.renderToSurface(outputBuffer, surface);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling renderToSurface without first calling setOutputMode(C.VIDEO_OUTPUT_MODE_SURFACE_YUV) on the decoder; calling it while the renderer operates in decoder-output-buffer mode; renderer called setDecoderOutputMode before the decoder instance existed, so the mode was never applied.

Common situations: Custom renderer code that renders to a SurfaceView-backed surface but never sets the output mode; switching between output modes mid-playback; using Libgav1VideoRenderer with a surface while another component forced buffer mode for screenshot/processing.

Related errors


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