{"record":{"id":"7645304dd19c75a4","repo":"google/ExoPlayer","slug":"buffer-render-error-gav1geterrormessage-gav1dec","errorCode":null,"errorMessage":"Buffer render error: ${gav1GetErrorMessage(gav1DecoderContext)}","messagePattern":"Buffer render error: (.+?)","errorType":"exception","errorClass":"Gav1DecoderException","httpStatus":null,"severity":"error","filePath":"extensions/av1/src/main/java/com/google/android/exoplayer2/ext/av1/Gav1Decoder.java","lineNumber":180,"sourceCode":"    this.outputMode = outputMode;\n  }\n\n  /**\n   * Renders output buffer to the given surface. Must only be called when in {@link\n   * C#VIDEO_OUTPUT_MODE_SURFACE_YUV} mode.\n   *\n   * @param outputBuffer Output buffer.\n   * @param surface Output surface.\n   * @throws Gav1DecoderException Thrown if called with invalid output mode or frame rendering\n   *     fails.\n   */\n  public void renderToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface)\n      throws Gav1DecoderException {\n    if (outputBuffer.mode != C.VIDEO_OUTPUT_MODE_SURFACE_YUV) {\n      throw new Gav1DecoderException(\"Invalid output mode.\");\n    }\n    if (gav1RenderFrame(gav1DecoderContext, surface, outputBuffer) == GAV1_ERROR) {\n      throw new Gav1DecoderException(\n          \"Buffer render error: \" + gav1GetErrorMessage(gav1DecoderContext));\n    }\n  }\n\n  /**\n   * Initializes a libgav1 decoder.\n   *\n   * @param threads Number of threads to be used by a libgav1 decoder.\n   * @return The address of the decoder context or {@link #GAV1_ERROR} if there was an error.\n   */\n  private native long gav1Init(int threads);\n\n  /**\n   * Deallocates the decoder context.\n   *\n   * @param context Decoder context.\n   */\n  private native void gav1Close(long context);","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/extensions/av1/src/main/java/com/google/android/exoplayer2/ext/av1/Gav1Decoder.java#L162-L198","documentation":"Gav1DecoderException thrown when the native gav1RenderFrame call returns GAV1_ERROR while pushing a decoded frame to the output surface. The appended gav1GetErrorMessage() text carries the native reason, typically an unusable surface, a bad frame, or an errored decoder context.","triggerScenarios":"The Surface passed to renderToSurface was released or its producer disconnected between renderer setup and frame render; the decoder context is in an error state from an earlier failed decode; EGL/SurfaceTexture backing the surface destroyed on a different thread.","commonSituations":"Activity teardown or surface destroyed mid-playback; switching surfaces while frames are in flight; codec surface detached by the UI layer concurrently with rendering.","solutions":["Read the native message text to distinguish 'surface invalid' from 'decoder errored'","Guarantee the surface stays valid while rendering: only release it after the renderer has been stopped/flushed","Recreate/flush the renderer (and re-init the decoder) when the surface changes instead of reusing the old context","Catch Gav1DecoderException in the render path and trigger renderer recovery rather than crashing the playback thread"],"exampleFix":"// before\ndecoder.renderToSurface(outputBuffer, surface);\noutputBuffer.release();\n\n// after: detect dead surfaces and recover\ntry {\n  decoder.renderToSurface(outputBuffer, surface);\n} catch (Gav1DecoderException e) {\n  Log.e(TAG, \"render failed: \" + e.getMessage(), e);\n  // signal the player to disable/re-enable the renderer with a fresh surface\n} finally {\n  outputBuffer.release();\n}","handlingStrategy":"try-catch","validationCode":"if (surface == null || !surface.isValid()) {\n  // do not render into a released/invalid surface\n  outputBuffer.release();\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  decoder.renderToSurface(outputBuffer, surface);\n} catch (Gav1DecoderException e) {\n  // native message via gav1GetErrorMessage() identifies surface vs decoder failure;\n  // signal renderer disable/re-enable to rebuild the decoder+surface pair\n  onRendererRecoveryNeeded(e);\n} finally {\n  outputBuffer.release();\n}","preventionTips":["Keep the surface alive until after the renderer is stopped or flushed","Recreate the decoder context when the output surface changes; do not reuse the old one","Check surface.isValid() before render attempts during teardown windows"],"tags":["android","av1","surface","rendering","native-library"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}