public interface

VideoRendererEventListener

 androidx.media3.exoplayer.video.VideoRendererEventListener

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.0.0-alpha03'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.0.0-alpha03

Artifact androidx.media3:media3-exoplayer:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

Listener of video Renderer events. All methods have no-op default implementations to allow selective overrides.

Summary

Methods
public voidonDroppedFrames(int count, long elapsedMs)

Called to report the number of frames dropped by the renderer.

public voidonRenderedFirstFrame(java.lang.Object output, long renderTimeMs)

Called when a frame is rendered for the first time since setting the output, or since the renderer was reset, or since the stream being rendered was changed.

public voidonVideoCodecError(java.lang.Exception videoCodecError)

Called when a video decoder encounters an error.

public voidonVideoDecoderInitialized(java.lang.String decoderName, long initializedTimestampMs, long initializationDurationMs)

Called when a decoder is created.

public voidonVideoDecoderReleased(java.lang.String decoderName)

Called when a decoder is released.

public voidonVideoDisabled(DecoderCounters counters)

Called when the renderer is disabled.

public voidonVideoEnabled(DecoderCounters counters)

Called when the renderer is enabled.

public voidonVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount)

Called to report the video processing offset of video frames processed by the video renderer.

public voidonVideoInputFormatChanged(Format format)

public voidonVideoInputFormatChanged(Format format, DecoderReuseEvaluation decoderReuseEvaluation)

Called when the format of the media being consumed by the renderer changes.

public voidonVideoSizeChanged(VideoSize videoSize)

Called before a frame is rendered for the first time since setting the surface, and each time there's a change in the size, rotation or pixel aspect ratio of the video being rendered.

Methods

public void onVideoEnabled(DecoderCounters counters)

Called when the renderer is enabled.

Parameters:

counters: DecoderCounters that will be updated by the renderer for as long as it remains enabled.

public void onVideoDecoderInitialized(java.lang.String decoderName, long initializedTimestampMs, long initializationDurationMs)

Called when a decoder is created.

Parameters:

decoderName: The decoder that was created.
initializedTimestampMs: when initialization finished.
initializationDurationMs: The time taken to initialize the decoder in milliseconds.

public void onVideoInputFormatChanged(Format format)

Deprecated: Use VideoRendererEventListener.onVideoInputFormatChanged(Format, DecoderReuseEvaluation).

public void onVideoInputFormatChanged(Format format, DecoderReuseEvaluation decoderReuseEvaluation)

Called when the format of the media being consumed by the renderer changes.

Parameters:

format: The new format.
decoderReuseEvaluation: The result of the evaluation to determine whether an existing decoder instance can be reused for the new format, or null if the renderer did not have a decoder.

public void onDroppedFrames(int count, long elapsedMs)

Called to report the number of frames dropped by the renderer. Dropped frames are reported whenever the renderer is stopped having dropped frames, and optionally, whenever the count reaches a specified threshold whilst the renderer is started.

Parameters:

count: The number of dropped frames.
elapsedMs: The duration in milliseconds over which the frames were dropped. This duration is timed from when the renderer was started or from when dropped frames were last reported (whichever was more recent), and not from when the first of the reported drops occurred.

public void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount)

Called to report the video processing offset of video frames processed by the video renderer.

Video processing offset represents how early a video frame is processed compared to the player's current position. For each video frame, the offset is calculated as Pvf - Ppl where Pvf is the presentation timestamp of the video frame and Ppl is the current position of the player. Positive values indicate the frame was processed early enough whereas negative values indicate that the player's position had progressed beyond the frame's timestamp when the frame was processed (and the frame was probably dropped).

The renderer reports the sum of video processing offset samples (one sample per processed video frame: dropped, skipped or rendered) and the total number of samples.

Parameters:

totalProcessingOffsetUs: The sum of all video frame processing offset samples for the video frames processed by the renderer in microseconds.
frameCount: The number of samples included in the totalProcessingOffsetUs.

public void onVideoSizeChanged(VideoSize videoSize)

Called before a frame is rendered for the first time since setting the surface, and each time there's a change in the size, rotation or pixel aspect ratio of the video being rendered.

Parameters:

videoSize: The new size of the video.

public void onRenderedFirstFrame(java.lang.Object output, long renderTimeMs)

Called when a frame is rendered for the first time since setting the output, or since the renderer was reset, or since the stream being rendered was changed.

Parameters:

output: The output of the video renderer. Normally a , however some video renderers may have other output types (e.g., a VideoDecoderOutputBufferRenderer).
renderTimeMs: The when the frame was rendered.

public void onVideoDecoderReleased(java.lang.String decoderName)

Called when a decoder is released.

Parameters:

decoderName: The decoder that was released.

public void onVideoDisabled(DecoderCounters counters)

Called when the renderer is disabled.

Parameters:

counters: DecoderCounters that were updated by the renderer.

public void onVideoCodecError(java.lang.Exception videoCodecError)

Called when a video decoder encounters an error.

This method being called does not indicate that playback has failed, or that it will fail. The player may be able to recover from the error. Hence applications should not implement this method to display a user visible error or initiate an application level retry. is the appropriate place to implement such behavior. This method is called to provide the application with an opportunity to log the error if it wishes to do so.

Parameters:

videoCodecError: The error. Typically a if the renderer uses MediaCodec, or a DecoderException if the renderer uses a software decoder.

Source

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.exoplayer.video;

import static androidx.media3.common.util.Util.castNonNull;

import android.media.MediaCodec;
import android.media.MediaCodec.CodecException;
import android.os.Handler;
import android.os.SystemClock;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.Format;
import androidx.media3.common.Player;
import androidx.media3.common.VideoSize;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.decoder.DecoderException;
import androidx.media3.exoplayer.DecoderCounters;
import androidx.media3.exoplayer.DecoderReuseEvaluation;
import androidx.media3.exoplayer.Renderer;

/**
 * Listener of video {@link Renderer} events. All methods have no-op default implementations to
 * allow selective overrides.
 */
@UnstableApi
public interface VideoRendererEventListener {

  /**
   * Called when the renderer is enabled.
   *
   * @param counters {@link DecoderCounters} that will be updated by the renderer for as long as it
   *     remains enabled.
   */
  default void onVideoEnabled(DecoderCounters counters) {}

  /**
   * Called when a decoder is created.
   *
   * @param decoderName The decoder that was created.
   * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization
   *     finished.
   * @param initializationDurationMs The time taken to initialize the decoder in milliseconds.
   */
  default void onVideoDecoderInitialized(
      String decoderName, long initializedTimestampMs, long initializationDurationMs) {}

  /** @deprecated Use {@link #onVideoInputFormatChanged(Format, DecoderReuseEvaluation)}. */
  @Deprecated
  default void onVideoInputFormatChanged(Format format) {}

  /**
   * Called when the format of the media being consumed by the renderer changes.
   *
   * @param format The new format.
   * @param decoderReuseEvaluation The result of the evaluation to determine whether an existing
   *     decoder instance can be reused for the new format, or {@code null} if the renderer did not
   *     have a decoder.
   */
  default void onVideoInputFormatChanged(
      Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {}

  /**
   * Called to report the number of frames dropped by the renderer. Dropped frames are reported
   * whenever the renderer is stopped having dropped frames, and optionally, whenever the count
   * reaches a specified threshold whilst the renderer is started.
   *
   * @param count The number of dropped frames.
   * @param elapsedMs The duration in milliseconds over which the frames were dropped. This duration
   *     is timed from when the renderer was started or from when dropped frames were last reported
   *     (whichever was more recent), and not from when the first of the reported drops occurred.
   */
  default void onDroppedFrames(int count, long elapsedMs) {}

  /**
   * Called to report the video processing offset of video frames processed by the video renderer.
   *
   * <p>Video processing offset represents how early a video frame is processed compared to the
   * player's current position. For each video frame, the offset is calculated as <em>P<sub>vf</sub>
   * - P<sub>pl</sub></em> where <em>P<sub>vf</sub></em> is the presentation timestamp of the video
   * frame and <em>P<sub>pl</sub></em> is the current position of the player. Positive values
   * indicate the frame was processed early enough whereas negative values indicate that the
   * player's position had progressed beyond the frame's timestamp when the frame was processed (and
   * the frame was probably dropped).
   *
   * <p>The renderer reports the sum of video processing offset samples (one sample per processed
   * video frame: dropped, skipped or rendered) and the total number of samples.
   *
   * @param totalProcessingOffsetUs The sum of all video frame processing offset samples for the
   *     video frames processed by the renderer in microseconds.
   * @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}.
   */
  default void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {}

  /**
   * Called before a frame is rendered for the first time since setting the surface, and each time
   * there's a change in the size, rotation or pixel aspect ratio of the video being rendered.
   *
   * @param videoSize The new size of the video.
   */
  default void onVideoSizeChanged(VideoSize videoSize) {}

  /**
   * Called when a frame is rendered for the first time since setting the output, or since the
   * renderer was reset, or since the stream being rendered was changed.
   *
   * @param output The output of the video renderer. Normally a {@link Surface}, however some video
   *     renderers may have other output types (e.g., a {@link VideoDecoderOutputBufferRenderer}).
   * @param renderTimeMs The {@link SystemClock#elapsedRealtime()} when the frame was rendered.
   */
  default void onRenderedFirstFrame(Object output, long renderTimeMs) {}

  /**
   * Called when a decoder is released.
   *
   * @param decoderName The decoder that was released.
   */
  default void onVideoDecoderReleased(String decoderName) {}

  /**
   * Called when the renderer is disabled.
   *
   * @param counters {@link DecoderCounters} that were updated by the renderer.
   */
  default void onVideoDisabled(DecoderCounters counters) {}

  /**
   * Called when a video decoder encounters an error.
   *
   * <p>This method being called does not indicate that playback has failed, or that it will fail.
   * The player may be able to recover from the error. Hence applications should <em>not</em>
   * implement this method to display a user visible error or initiate an application level retry.
   * {@link Player.Listener#onPlayerError} is the appropriate place to implement such behavior. This
   * method is called to provide the application with an opportunity to log the error if it wishes
   * to do so.
   *
   * @param videoCodecError The error. Typically a {@link CodecException} if the renderer uses
   *     {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder.
   */
  default void onVideoCodecError(Exception videoCodecError) {}

  /** Dispatches events to a {@link VideoRendererEventListener}. */
  final class EventDispatcher {

    @Nullable private final Handler handler;
    @Nullable private final VideoRendererEventListener listener;

    /**
     * @param handler A handler for dispatching events, or null if events should not be dispatched.
     * @param listener The listener to which events should be dispatched, or null if events should
     *     not be dispatched.
     */
    public EventDispatcher(
        @Nullable Handler handler, @Nullable VideoRendererEventListener listener) {
      this.handler = listener != null ? Assertions.checkNotNull(handler) : null;
      this.listener = listener;
    }

    /** Invokes {@link VideoRendererEventListener#onVideoEnabled(DecoderCounters)}. */
    public void enabled(DecoderCounters decoderCounters) {
      if (handler != null) {
        handler.post(() -> castNonNull(listener).onVideoEnabled(decoderCounters));
      }
    }

    /** Invokes {@link VideoRendererEventListener#onVideoDecoderInitialized(String, long, long)}. */
    public void decoderInitialized(
        String decoderName, long initializedTimestampMs, long initializationDurationMs) {
      if (handler != null) {
        handler.post(
            () ->
                castNonNull(listener)
                    .onVideoDecoderInitialized(
                        decoderName, initializedTimestampMs, initializationDurationMs));
      }
    }

    /**
     * Invokes {@link VideoRendererEventListener#onVideoInputFormatChanged(Format,
     * DecoderReuseEvaluation)}.
     */
    @SuppressWarnings("deprecation") // Calling deprecated listener method.
    public void inputFormatChanged(
        Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {
      if (handler != null) {
        handler.post(
            () -> {
              castNonNull(listener).onVideoInputFormatChanged(format);
              castNonNull(listener).onVideoInputFormatChanged(format, decoderReuseEvaluation);
            });
      }
    }

    /** Invokes {@link VideoRendererEventListener#onDroppedFrames(int, long)}. */
    public void droppedFrames(int droppedFrameCount, long elapsedMs) {
      if (handler != null) {
        handler.post(() -> castNonNull(listener).onDroppedFrames(droppedFrameCount, elapsedMs));
      }
    }

    /** Invokes {@link VideoRendererEventListener#onVideoFrameProcessingOffset}. */
    public void reportVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
      if (handler != null) {
        handler.post(
            () ->
                castNonNull(listener)
                    .onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount));
      }
    }

    /** Invokes {@link VideoRendererEventListener#onVideoSizeChanged(VideoSize)}. */
    public void videoSizeChanged(VideoSize videoSize) {
      if (handler != null) {
        handler.post(() -> castNonNull(listener).onVideoSizeChanged(videoSize));
      }
    }

    /** Invokes {@link VideoRendererEventListener#onRenderedFirstFrame(Object, long)}. */
    public void renderedFirstFrame(Object output) {
      if (handler != null) {
        // TODO: Replace this timestamp with the actual frame release time.
        long renderTimeMs = SystemClock.elapsedRealtime();
        handler.post(() -> castNonNull(listener).onRenderedFirstFrame(output, renderTimeMs));
      }
    }

    /** Invokes {@link VideoRendererEventListener#onVideoDecoderReleased(String)}. */
    public void decoderReleased(String decoderName) {
      if (handler != null) {
        handler.post(() -> castNonNull(listener).onVideoDecoderReleased(decoderName));
      }
    }

    /** Invokes {@link VideoRendererEventListener#onVideoDisabled(DecoderCounters)}. */
    public void disabled(DecoderCounters counters) {
      counters.ensureUpdated();
      if (handler != null) {
        handler.post(
            () -> {
              counters.ensureUpdated();
              castNonNull(listener).onVideoDisabled(counters);
            });
      }
    }

    /** Invokes {@link VideoRendererEventListener#onVideoCodecError(Exception)}. */
    public void videoCodecError(Exception videoCodecError) {
      if (handler != null) {
        handler.post(() -> castNonNull(listener).onVideoCodecError(videoCodecError));
      }
    }
  }
}