public class

DebugTextViewHelper

extends java.lang.Object

 java.lang.Object

↳androidx.media3.exoplayer.util.DebugTextViewHelper

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-exoplayer:1.5.0-alpha01 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 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.

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.annotation.Nullable;
import androidx.media3.common.ColorInfo;
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.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}.
 */
public class DebugTextViewHelper {

  private static final int REFRESH_INTERVAL_MS = 1000;

  private final ExoPlayer player;
  private final TextView textView;
  private final Updater updater;

  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;
    this.updater = new Updater();
  }

  /**
   * 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(updater);
    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(updater);
    textView.removeCallbacks(updater);
  }

  // Protected methods.

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

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

  /** Returns a string containing player state debugging information. */
  @UnstableApi
  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. */
  @UnstableApi
  protected String getVideoString() {
    Format format = player.getVideoFormat();
    VideoSize videoSize = player.getVideoSize();
    DecoderCounters decoderCounters = player.getVideoDecoderCounters();
    if (format == null || decoderCounters == null) {
      return "";
    }
    return "\n"
        + format.sampleMimeType
        + "(id:"
        + format.id
        + " r:"
        + videoSize.width
        + "x"
        + videoSize.height
        + getColorInfoString(format.colorInfo)
        + getPixelAspectRatioString(videoSize.pixelWidthHeightRatio)
        + getDecoderCountersBufferCountString(decoderCounters)
        + " vfpo: "
        + getVideoFrameProcessingOffsetAverageString(
            decoderCounters.totalVideoFrameProcessingOffsetUs,
            decoderCounters.videoFrameProcessingOffsetCount)
        + ")";
  }

  /** Returns a string containing audio debugging information. */
  @UnstableApi
  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 getColorInfoString(@Nullable ColorInfo colorInfo) {
    return colorInfo != null && colorInfo.isValid() ? " colr:" + colorInfo.toLogString() : "";
  }

  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);
    }
  }

  private final class Updater implements Player.Listener, Runnable {

    // Player.Listener implementation.

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

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

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

    // Runnable implementation.

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