public class

DebugTextViewHelper

extends java.lang.Object

implements Player.Listener, java.lang.Runnable

 java.lang.Object

↳androidx.media3.exoplayer.util.DebugTextViewHelper

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

A helper class for periodically updating a TextView with debug information obtained from an ExoPlayer.

Summary

Constructors
publicDebugTextViewHelper(ExoPlayer player, TextView textView)

Methods
protected java.lang.StringgetAudioString()

Returns a string containing audio debugging information.

protected java.lang.StringgetDebugString()

Returns the debugging information string to be shown by the target TextView.

protected java.lang.StringgetPlayerStateString()

Returns a string containing player state debugging information.

protected java.lang.StringgetVideoString()

Returns a string containing video debugging information.

public final voidonPlaybackStateChanged(int playbackState)

public final voidonPlayWhenReadyChanged(boolean playWhenReady, int reason)

public final voidonPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason)

public final voidrun()

public final voidstart()

Starts periodic updates of the TextView.

public final voidstop()

Stops periodic updates of the TextView.

protected final voidupdateAndPost()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public DebugTextViewHelper(ExoPlayer player, TextView textView)

Parameters:

player: The ExoPlayer from which debug information should be obtained. Only players which are accessed on the main thread are supported (player.getApplicationLooper() == Looper.getMainLooper()).
textView: The TextView that should be updated to display the information.

Methods

public final void start()

Starts periodic updates of the TextView. Must be called from the application's main thread.

public final void stop()

Stops periodic updates of the TextView. Must be called from the application's main thread.

public final void onPlaybackStateChanged(int playbackState)

public final void onPlayWhenReadyChanged(boolean playWhenReady, int reason)

public final void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason)

public final void run()

protected final void updateAndPost()

protected java.lang.String getDebugString()

Returns the debugging information string to be shown by the target TextView.

protected java.lang.String getPlayerStateString()

Returns a string containing player state debugging information.

protected java.lang.String getVideoString()

Returns a string containing video debugging information.

protected java.lang.String getAudioString()

Returns a string containing audio debugging information.

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.util;

import android.annotation.SuppressLint;
import android.os.Looper;
import android.widget.TextView;
import androidx.media3.common.Format;
import androidx.media3.common.Player;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.DecoderCounters;
import androidx.media3.exoplayer.ExoPlayer;
import java.util.Locale;

/**
 * A helper class for periodically updating a {@link TextView} with debug information obtained from
 * an {@link ExoPlayer}.
 */
@UnstableApi
public class DebugTextViewHelper implements Player.Listener, Runnable {

  private static final int REFRESH_INTERVAL_MS = 1000;

  private final ExoPlayer player;
  private final TextView textView;

  private boolean started;

  /**
   * @param player The {@link ExoPlayer} from which debug information should be obtained. Only
   *     players which are accessed on the main thread are supported ({@code
   *     player.getApplicationLooper() == Looper.getMainLooper()}).
   * @param textView The {@link TextView} that should be updated to display the information.
   */
  public DebugTextViewHelper(ExoPlayer player, TextView textView) {
    Assertions.checkArgument(player.getApplicationLooper() == Looper.getMainLooper());
    this.player = player;
    this.textView = textView;
  }

  /**
   * Starts periodic updates of the {@link TextView}. Must be called from the application's main
   * thread.
   */
  public final void start() {
    if (started) {
      return;
    }
    started = true;
    player.addListener(this);
    updateAndPost();
  }

  /**
   * Stops periodic updates of the {@link TextView}. Must be called from the application's main
   * thread.
   */
  public final void stop() {
    if (!started) {
      return;
    }
    started = false;
    player.removeListener(this);
    textView.removeCallbacks(this);
  }

  // Player.Listener implementation.

  @Override
  public final void onPlaybackStateChanged(@Player.State int playbackState) {
    updateAndPost();
  }

  @Override
  public final void onPlayWhenReadyChanged(
      boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) {
    updateAndPost();
  }

  @Override
  public final void onPositionDiscontinuity(
      Player.PositionInfo oldPosition,
      Player.PositionInfo newPosition,
      @Player.DiscontinuityReason int reason) {
    updateAndPost();
  }

  // Runnable implementation.

  @Override
  public final void run() {
    updateAndPost();
  }

  // Protected methods.

  @SuppressLint("SetTextI18n")
  protected final void updateAndPost() {
    textView.setText(getDebugString());
    textView.removeCallbacks(this);
    textView.postDelayed(this, REFRESH_INTERVAL_MS);
  }

  /** Returns the debugging information string to be shown by the target {@link TextView}. */
  protected String getDebugString() {
    return getPlayerStateString() + getVideoString() + getAudioString();
  }

  /** Returns a string containing player state debugging information. */
  protected String getPlayerStateString() {
    String playbackStateString;
    switch (player.getPlaybackState()) {
      case Player.STATE_BUFFERING:
        playbackStateString = "buffering";
        break;
      case Player.STATE_ENDED:
        playbackStateString = "ended";
        break;
      case Player.STATE_IDLE:
        playbackStateString = "idle";
        break;
      case Player.STATE_READY:
        playbackStateString = "ready";
        break;
      default:
        playbackStateString = "unknown";
        break;
    }
    return String.format(
        "playWhenReady:%s playbackState:%s item:%s",
        player.getPlayWhenReady(), playbackStateString, player.getCurrentMediaItemIndex());
  }

  /** Returns a string containing video debugging information. */
  protected String getVideoString() {
    Format format = player.getVideoFormat();
    DecoderCounters decoderCounters = player.getVideoDecoderCounters();
    if (format == null || decoderCounters == null) {
      return "";
    }
    return "\n"
        + format.sampleMimeType
        + "(id:"
        + format.id
        + " r:"
        + format.width
        + "x"
        + format.height
        + getPixelAspectRatioString(format.pixelWidthHeightRatio)
        + getDecoderCountersBufferCountString(decoderCounters)
        + " vfpo: "
        + getVideoFrameProcessingOffsetAverageString(
            decoderCounters.totalVideoFrameProcessingOffsetUs,
            decoderCounters.videoFrameProcessingOffsetCount)
        + ")";
  }

  /** Returns a string containing audio debugging information. */
  protected String getAudioString() {
    Format format = player.getAudioFormat();
    DecoderCounters decoderCounters = player.getAudioDecoderCounters();
    if (format == null || decoderCounters == null) {
      return "";
    }
    return "\n"
        + format.sampleMimeType
        + "(id:"
        + format.id
        + " hz:"
        + format.sampleRate
        + " ch:"
        + format.channelCount
        + getDecoderCountersBufferCountString(decoderCounters)
        + ")";
  }

  private static String getDecoderCountersBufferCountString(DecoderCounters counters) {
    if (counters == null) {
      return "";
    }
    counters.ensureUpdated();
    return " sib:"
        + counters.skippedInputBufferCount
        + " sb:"
        + counters.skippedOutputBufferCount
        + " rb:"
        + counters.renderedOutputBufferCount
        + " db:"
        + counters.droppedBufferCount
        + " mcdb:"
        + counters.maxConsecutiveDroppedBufferCount
        + " dk:"
        + counters.droppedToKeyframeCount;
  }

  private static String getPixelAspectRatioString(float pixelAspectRatio) {
    return pixelAspectRatio == Format.NO_VALUE || pixelAspectRatio == 1f
        ? ""
        : (" par:" + String.format(Locale.US, "%.02f", pixelAspectRatio));
  }

  private static String getVideoFrameProcessingOffsetAverageString(
      long totalOffsetUs, int frameCount) {
    if (frameCount == 0) {
      return "N/A";
    } else {
      long averageUs = (long) ((double) totalOffsetUs / frameCount);
      return String.valueOf(averageUs);
    }
  }
}