google/ExoPlayer · error · IllegalArgumentException

Unsupported color transfer: {colorTransfer}

Error message

Unsupported color transfer: {colorTransfer}

What it means

Thrown by GlUtil.createEglSurface(...) when the requested C.COLOR_TRANSFER value is neither SDR (gamma) nor one of the two supported HDR transfers (SMPTE 2084 PQ handled above, or HLG handled just before). This argument must be one of the C.COLOR_TRANSFER_* constants; arbitrary integers or ColorInfo values with an unknown transfer function are rejected before EGL surface creation.

Source

Thrown at library/common/src/main/java/com/google/android/exoplayer2/util/GlUtil.java:328

      windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_NONE;
    } else if (colorTransfer == C.COLOR_TRANSFER_ST2084) {
      configAttributes = EGL_CONFIG_ATTRIBUTES_RGBA_1010102;
      if (isEncoderInputSurface) {
        // Outputting BT2020 PQ with EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ to an encoder causes
        // the encoder to incorrectly switch to full range color, even if the encoder is configured
        // with limited range color, because EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ sets full range
        // color output, and GL windowAttributes overrides encoder settings.
        windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_NONE;
      } else {
        // TODO(b/262259999) HDR10 PQ content looks dark on the screen.
        windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ;
      }
    } else if (colorTransfer == C.COLOR_TRANSFER_HLG) {
      checkArgument(isEncoderInputSurface, "Outputting HLG to the screen is not supported.");
      configAttributes = EGL_CONFIG_ATTRIBUTES_RGBA_1010102;
      windowAttributes = EGL_WINDOW_SURFACE_ATTRIBUTES_NONE;
    } else {
      throw new IllegalArgumentException("Unsupported color transfer: " + colorTransfer);
    }
    return Api17.createEglSurface(eglDisplay, surface, configAttributes, windowAttributes);
  }

  /**
   * Creates a new {@link EGLSurface} wrapping a pixel buffer.
   *
   * @param eglDisplay The {@link EGLDisplay} to attach the surface to.
   * @param width The width of the pixel buffer.
   * @param height The height of the pixel buffer.
   * @param configAttributes EGL configuration attributes. Valid arguments include {@link
   *     #EGL_CONFIG_ATTRIBUTES_RGBA_8888} and {@link #EGL_CONFIG_ATTRIBUTES_RGBA_1010102}.
   */
  @RequiresApi(17)
  private static EGLSurface createPbufferSurface(
      EGLDisplay eglDisplay, int width, int height, int[] configAttributes) throws GlException {
    int[] pbufferAttributes =
        new int[] {

View on GitHub (pinned to dd430f7053)

Solutions

  1. Map or clamp the transfer before calling: treat COLOR_TRANSFER_UNKNOWN/LINEAR/GAMMA_2_2 as C.COLOR_TRANSFER_SDR_VIDEO (or skip EGL HDR path)
  2. Apply tone mapping (or force SDR) when the source transfer is unsupported: set the experimental tone-mapping setting in DefaultRenderersFactory, or transcode to SDR first
  3. Validate ColorInfo.colorTransfer against the supported set {COLOR_TRANSFER_SDR_VIDEO, COLOR_TRANSFER_SDR_GAMMA_2_2 (SDR path), COLOR_TRANSFER_HLG, COLOR_TRANSFER_SMPTE_170M/2084 as handled} and fail with a clear upstream message before touching EGL
  4. Upgrade media3 — HDR/EGL transfer support expands across versions (b/262259999 and friends track added transfers)

Example fix

// before
EGLSurface surface = GlUtil.createEglSurface(eglDisplay, outputSurface, colorInfo.colorTransfer);
// after
int transfer = colorInfo.colorTransfer;
boolean hdrOk = transfer == C.COLOR_TRANSFER_SDR_VIDEO
    || transfer == C.COLOR_TRANSFER_HLG
    || transfer == C.COLOR_TRANSFER_SMPTE_2084;
int safeTransfer = hdrOk ? transfer : C.COLOR_TRANSFER_SDR_VIDEO;
EGLSurface surface = GlUtil.createEglSurface(eglDisplay, outputSurface, safeTransfer);
Defensive patterns

Strategy: type-guard

Validate before calling

private static final Set<Integer> SUPPORTED = Set.of(
    C.COLOR_TRANSFER_SDR_VIDEO, C.COLOR_TRANSFER_SDR_GAMMA_2_2,
    C.COLOR_TRANSFER_HLG, C.COLOR_TRANSFER_SMPTE_2084);
int transfer = SUPPORTED.contains(colorInfo.colorTransfer)
    ? colorInfo.colorTransfer : C.COLOR_TRANSFER_SDR_VIDEO;

Type guard

static boolean isSupportedColorTransfer(@Nullable ColorInfo info) {
  return info != null && (info.colorTransfer == C.COLOR_TRANSFER_SDR_VIDEO
      || info.colorTransfer == C.COLOR_TRANSFER_SDR_GAMMA_2_2
      || info.colorTransfer == C.COLOR_TRANSFER_HLG
      || info.colorTransfer == C.COLOR_TRANSFER_SMPTE_2084);
}

Try / catch

catch (IllegalArgumentException e) { /* downgrade transfer to SDR and retry createEglSurface */ }

Prevention

When it happens

Trigger: Passing colorTransfer from ColorInfo.colorTransfer when it is COLOR_TRANSFER_SDR_VIDEO/HLG is fine, but a value like C.COLOR_TRANSFER_LINEAR, C.COLOR_TRANSFER_GAMMA_2_2, an unrecognized vendor transfer, or COLOR_TRANSFER_UNKNOWN (-1) reaches the final else and throws IllegalArgumentException. Also, HLG on a non-encoder (display) surface throws earlier via checkArgument, not here.

Common situations: Building a transformer/effects pipeline that copies ColorInfo from arbitrary source media (some cameras tag LINEAR or GAMMA_2_2), forwarding ToneMapping-heavy or AV1-HDR metadata into GlUtil.createEglSurface, or passing 0/-1 defaults from an uninitialized ColorInfo instead of a concrete transfer constant.

Related errors


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