{"record":{"id":"7d3c36ac69609652","repo":"google/ExoPlayer","slug":"missing-implementation-to-handle-command-set-video","errorCode":null,"errorMessage":"Missing implementation to handle COMMAND_SET_VIDEO_SURFACE","messagePattern":"Missing implementation to handle COMMAND_SET_VIDEO_SURFACE","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java","lineNumber":3144,"sourceCode":"  protected ListenableFuture<?> handleSetDeviceMuted(boolean muted, @C.VolumeFlags int flags) {\n    throw new IllegalStateException(\n        \"Missing implementation to handle COMMAND_ADJUST_DEVICE_VOLUME or\"\n            + \" COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS\");\n  }\n\n  /**\n   * Handles calls to set the video output.\n   *\n   * <p>Will only be called if {@link Player#COMMAND_SET_VIDEO_SURFACE} is available.\n   *\n   * @param videoOutput The requested video output. This is either a {@link Surface}, {@link\n   *     SurfaceHolder}, {@link TextureView} or {@link SurfaceView}.\n   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}\n   *     changes caused by this call.\n   */\n  @ForOverride\n  protected ListenableFuture<?> handleSetVideoOutput(Object videoOutput) {\n    throw new IllegalStateException(\"Missing implementation to handle COMMAND_SET_VIDEO_SURFACE\");\n  }\n\n  /**\n   * Handles calls to clear the video output.\n   *\n   * <p>Will only be called if {@link Player#COMMAND_SET_VIDEO_SURFACE} is available.\n   *\n   * @param videoOutput The video output to clear. If null any current output should be cleared. If\n   *     non-null, the output should only be cleared if it matches the provided argument. This is\n   *     either a {@link Surface}, {@link SurfaceHolder}, {@link TextureView} or {@link\n   *     SurfaceView}.\n   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}\n   *     changes caused by this call.\n   */\n  @ForOverride\n  protected ListenableFuture<?> handleClearVideoOutput(@Nullable Object videoOutput) {\n    throw new IllegalStateException(\"Missing implementation to handle COMMAND_SET_VIDEO_SURFACE\");\n  }","sourceCodeStart":3126,"sourceCodeEnd":3162,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java#L3126-L3162","documentation":"Player.setVideoSurface/setVideoSurfaceHolder/setVideoTextureView/etc. all funnel through SimpleBasePlayer.handleSetVideoOutput(Object). The default implementation throws IllegalStateException when COMMAND_SET_VIDEO_SURFACE is present in availableCommands but the subclass never overrode the handler. videoOutput is the opaque Surface/SurfaceHolder/TextureView/SurfaceView instance the caller supplied.","triggerScenarios":"Calling player.setVideoSurface(surface) (or any setVideo* variant) on a custom SimpleBasePlayer that declares COMMAND_SET_VIDEO_SURFACE in State.availableCommands without overriding handleSetVideoOutput.","commonSituations":"Building an audio-focused custom player but copying a video-capable command set; wrapping a codec or external playback engine and forgetting to attach the surface to it.","solutions":["Override handleSetVideoOutput(Object videoOutput), unwrap the Surface (via SurfaceHolder.getSurface() or TextureView-aware helpers) and pass it to your rendering backend; return an immediate future.","Also override handleClearVideoOutput since the same command gates it.","For audio-only players, exclude COMMAND_SET_VIDEO_SURFACE from availableCommands so video calls are ignored."],"exampleFix":"// before\n// availableCommands includes COMMAND_SET_VIDEO_SURFACE, no override:\n\n// after\n@Override\nprotected ListenableFuture<?> handleSetVideoOutput(Object videoOutput) {\n  if (videoOutput instanceof Surface) {\n    renderer.setOutputSurface((Surface) videoOutput);\n  } else if (videoOutput instanceof SurfaceHolder) {\n    renderer.setOutputSurface(((SurfaceHolder) videoOutput).getSurface());\n  }\n  return Futures.immediateVoidFuture();\n}","handlingStrategy":"validation","validationCode":"if (player.isCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) {\n  checkOverrides(playerClass, \"handleSetVideoOutput\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Implement set and clear video-output handlers as a pair.","For audio-only custom players never advertise COMMAND_SET_VIDEO_SURFACE.","Handle every videoOutput variant (Surface, SurfaceHolder, TextureView, SurfaceView) or explicitly ignore unsupported types."],"tags":["exoplayer","media3","player","video-surface","subclassing"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}