Improve logging II

- Show renderers with no tracks in EventLogger track logging
- Log renderer names in EventLogger track logging
- Add useful message to ExoPlaybackException instances (including
  renderer name for renderer errors)

PiperOrigin-RevId: 302421616
This commit is contained in:
olly 2020-03-23 13:35:31 +00:00 committed by Oliver Woodman
parent adae53c787
commit dc33c0bdfb
24 changed files with 224 additions and 44 deletions

View File

@ -34,6 +34,7 @@ import com.google.android.exoplayer2.video.VideoRendererEventListener;
/** Decodes and renders video using libgav1 decoder. */
public class Libgav1VideoRenderer extends DecoderVideoRenderer {
private static final String TAG = "Libgav1VideoRenderer";
private static final int DEFAULT_NUM_OF_INPUT_BUFFERS = 4;
private static final int DEFAULT_NUM_OF_OUTPUT_BUFFERS = 4;
/* Default size based on 720p resolution video compressed by a factor of two. */
@ -106,6 +107,11 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
this.numOutputBuffers = numOutputBuffers;
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public final int supportsFormat(Format format) {

View File

@ -32,6 +32,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Decodes and renders audio using FFmpeg. */
public final class FfmpegAudioRenderer extends DecoderAudioRenderer {
private static final String TAG = "FfmpegAudioRenderer";
/** The number of input and output buffers. */
private static final int NUM_BUFFERS = 16;
/** The default input buffer size. */
@ -88,6 +90,11 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer {
this.enableFloatOutput = enableFloatOutput;
}
@Override
public String getName() {
return TAG;
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {

View File

@ -36,6 +36,8 @@ import com.google.android.exoplayer2.video.VideoRendererEventListener;
*/
public final class FfmpegVideoRenderer extends DecoderVideoRenderer {
private static final String TAG = "FfmpegAudioRenderer";
/**
* Creates a new instance.
*
@ -56,6 +58,11 @@ public final class FfmpegVideoRenderer extends DecoderVideoRenderer {
// TODO: Implement.
}
@Override
public String getName() {
return TAG;
}
@Override
@RendererCapabilities.Capabilities
public final int supportsFormat(Format format) {

View File

@ -34,6 +34,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Decodes and renders audio using the native Flac decoder. */
public final class LibflacAudioRenderer extends DecoderAudioRenderer {
private static final String TAG = "LibflacAudioRenderer";
private static final int NUM_BUFFERS = 16;
private @MonotonicNonNull FlacStreamMetadata streamMetadata;
@ -71,6 +72,11 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer {
audioSink);
}
@Override
public String getName() {
return TAG;
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {

View File

@ -28,6 +28,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
/** Decodes and renders audio using the native Opus decoder. */
public class LibopusAudioRenderer extends DecoderAudioRenderer {
private static final String TAG = "LibopusAudioRenderer";
/** The number of input and output buffers. */
private static final int NUM_BUFFERS = 16;
/** The default input buffer size. */
@ -53,6 +54,11 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer {
super(eventHandler, eventListener, audioProcessors);
}
@Override
public String getName() {
return TAG;
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {

View File

@ -33,6 +33,8 @@ import com.google.android.exoplayer2.video.VideoRendererEventListener;
/** Decodes and renders video using the native VP9 decoder. */
public class LibvpxVideoRenderer extends DecoderVideoRenderer {
private static final String TAG = "LibvpxVideoRenderer";
/** The number of input buffers. */
private final int numInputBuffers;
/**
@ -115,6 +117,11 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
this.numOutputBuffers = numOutputBuffers;
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public final int supportsFormat(Format format) {

View File

@ -330,7 +330,8 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
throwRendererExceptionIsExecuting = false;
}
}
return ExoPlaybackException.createForRenderer(cause, getIndex(), format, formatSupport);
return ExoPlaybackException.createForRenderer(
cause, getName(), getIndex(), format, formatSupport);
}
/**

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2;
import android.os.SystemClock;
import android.text.TextUtils;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.RendererCapabilities.FormatSupport;
@ -70,9 +71,10 @@ public final class ExoPlaybackException extends Exception {
/** The {@link Type} of the playback failure. */
@Type public final int type;
/**
* If {@link #type} is {@link #TYPE_RENDERER}, this is the index of the renderer.
*/
/** If {@link #type} is {@link #TYPE_RENDERER}, this is the name of the renderer. */
@Nullable public final String rendererName;
/** If {@link #type} is {@link #TYPE_RENDERER}, this is the index of the renderer. */
public final int rendererIndex;
/**
@ -116,12 +118,15 @@ public final class ExoPlaybackException extends Exception {
*/
public static ExoPlaybackException createForRenderer(
Exception cause,
String rendererName,
int rendererIndex,
@Nullable Format rendererFormat,
@FormatSupport int rendererFormatSupport) {
return new ExoPlaybackException(
TYPE_RENDERER,
cause,
/* customMessage= */ null,
rendererName,
rendererIndex,
rendererFormat,
rendererFormat == null ? RendererCapabilities.FORMAT_HANDLED : rendererFormatSupport);
@ -161,6 +166,19 @@ public final class ExoPlaybackException extends Exception {
this(
type,
cause,
/* customMessage= */ null,
/* rendererName= */ null,
/* rendererIndex= */ C.INDEX_UNSET,
/* rendererFormat= */ null,
/* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED);
}
private ExoPlaybackException(@Type int type, String message) {
this(
type,
/* cause= */ null,
/* customMessage= */ message,
/* rendererName= */ null,
/* rendererIndex= */ C.INDEX_UNSET,
/* rendererFormat= */ null,
/* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED);
@ -168,29 +186,30 @@ public final class ExoPlaybackException extends Exception {
private ExoPlaybackException(
@Type int type,
Throwable cause,
@Nullable Throwable cause,
@Nullable String customMessage,
@Nullable String rendererName,
int rendererIndex,
@Nullable Format rendererFormat,
@FormatSupport int rendererFormatSupport) {
super(cause);
super(
deriveMessage(
type,
customMessage,
rendererName,
rendererIndex,
rendererFormat,
rendererFormatSupport),
cause);
this.type = type;
this.cause = cause;
this.rendererName = rendererName;
this.rendererIndex = rendererIndex;
this.rendererFormat = rendererFormat;
this.rendererFormatSupport = rendererFormatSupport;
timestampMs = SystemClock.elapsedRealtime();
}
private ExoPlaybackException(@Type int type, String message) {
super(message);
this.type = type;
rendererIndex = C.INDEX_UNSET;
rendererFormat = null;
rendererFormatSupport = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
cause = null;
timestampMs = SystemClock.elapsedRealtime();
}
/**
* Retrieves the underlying error when {@link #type} is {@link #TYPE_SOURCE}.
*
@ -230,4 +249,45 @@ public final class ExoPlaybackException extends Exception {
Assertions.checkState(type == TYPE_OUT_OF_MEMORY);
return (OutOfMemoryError) Assertions.checkNotNull(cause);
}
@Nullable
private static String deriveMessage(
@Type int type,
@Nullable String customMessage,
@Nullable String rendererName,
int rendererIndex,
@Nullable Format rendererFormat,
@FormatSupport int rendererFormatSupport) {
@Nullable String message;
switch (type) {
case TYPE_SOURCE:
message = "Source error";
break;
case TYPE_RENDERER:
message =
rendererName
+ " error"
+ ", index="
+ rendererIndex
+ ", format="
+ rendererFormat
+ ", format_supported="
+ RendererCapabilities.getFormatSupportString(rendererFormatSupport);
break;
case TYPE_REMOTE:
message = "Remote error";
break;
case TYPE_OUT_OF_MEMORY:
message = "Out of memory error";
break;
case TYPE_UNEXPECTED:
default:
message = "Unexpected runtime error";
break;
}
if (!TextUtils.isEmpty(customMessage)) {
message += ": " + customMessage;
}
return message;
}
}

View File

@ -379,7 +379,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Handler.Callback implementation.
@Override
@SuppressWarnings("unchecked")
public boolean handleMessage(Message msg) {
try {
switch (msg.what) {
@ -469,7 +468,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
maybeNotifyPlaybackInfoChanged();
} catch (ExoPlaybackException e) {
Log.e(TAG, getExoPlaybackExceptionMessage(e), e);
Log.e(TAG, "Playback error", e);
stopInternal(
/* forceResetRenderers= */ true,
/* resetPositionAndState= */ false,
@ -477,19 +476,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo = playbackInfo.copyWithPlaybackError(e);
maybeNotifyPlaybackInfoChanged();
} catch (IOException e) {
Log.e(TAG, "Source error", e);
ExoPlaybackException error = ExoPlaybackException.createForSource(e);
Log.e(TAG, "Playback error", error);
stopInternal(
/* forceResetRenderers= */ false,
/* resetPositionAndState= */ false,
/* acknowledgeStop= */ false);
playbackInfo = playbackInfo.copyWithPlaybackError(ExoPlaybackException.createForSource(e));
playbackInfo = playbackInfo.copyWithPlaybackError(error);
maybeNotifyPlaybackInfoChanged();
} catch (RuntimeException | OutOfMemoryError e) {
Log.e(TAG, "Internal runtime error", e);
ExoPlaybackException error =
e instanceof OutOfMemoryError
? ExoPlaybackException.createForOutOfMemoryError((OutOfMemoryError) e)
: ExoPlaybackException.createForUnexpected((RuntimeException) e);
Log.e(TAG, "Playback error", error);
stopInternal(
/* forceResetRenderers= */ true,
/* resetPositionAndState= */ false,
@ -502,20 +502,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Private methods.
private String getExoPlaybackExceptionMessage(ExoPlaybackException e) {
if (e.type != ExoPlaybackException.TYPE_RENDERER) {
return "Playback error.";
}
return "Renderer error: index="
+ e.rendererIndex
+ ", type="
+ Util.getTrackTypeString(renderers[e.rendererIndex].getTrackType())
+ ", format="
+ e.rendererFormat
+ ", rendererSupport="
+ RendererCapabilities.getFormatSupportString(e.rendererFormatSupport);
}
/**
* Blocks the current thread until {@link #releaseInternal()} is executed on the playback Thread.
*

View File

@ -189,6 +189,14 @@ public interface Renderer extends PlayerMessage.Target {
*/
int STATE_STARTED = 2;
/**
* Returns the name of this renderer, for logging and debugging purposes. Should typically be the
* renderer's (un-obfuscated) class name.
*
* @return The name of this renderer.
*/
String getName();
/**
* Returns the track type that the renderer handles. For example, a video renderer will return
* {@link C#TRACK_TYPE_VIDEO}, an audio renderer will return {@link C#TRACK_TYPE_AUDIO}, a text

View File

@ -260,6 +260,9 @@ public interface RendererCapabilities {
}
}
/** Returns the name of the {@link Renderer}. */
String getName();
/**
* Returns the track type that the {@link Renderer} handles. For example, a video renderer will
* return {@link C#TRACK_TYPE_VIDEO}, an audio renderer will return {@link C#TRACK_TYPE_AUDIO}, a

View File

@ -201,11 +201,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
audioSink.setListener(new AudioSinkListener());
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
protected int supportsFormat(
MediaCodecSelector mediaCodecSelector,
Format format)
protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
throws DecoderQueryException {
String mimeType = format.sampleMimeType;
if (!MimeTypes.isAudio(mimeType)) {

View File

@ -40,6 +40,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
*/
public final class MetadataRenderer extends BaseRenderer implements Callback {
private static final String TAG = "MetadataRenderer";
private static final int MSG_INVOKE_RENDERER = 0;
// TODO: Holding multiple pending metadata objects is temporary mitigation against
// https://github.com/google/ExoPlayer/issues/1874. It should be removed once this issue has been
@ -92,6 +93,11 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
pendingMetadataTimestamps = new long[MAX_PENDING_METADATA_COUNT];
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public int supportsFormat(Format format) {

View File

@ -121,6 +121,11 @@ public final class TextRenderer extends BaseRenderer implements Callback {
formatHolder = new FormatHolder();
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public int supportsFormat(Format format) {

View File

@ -92,6 +92,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
@Deprecated public final int length;
private final int rendererCount;
private final String[] rendererNames;
private final int[] rendererTrackTypes;
private final TrackGroupArray[] rendererTrackGroups;
@AdaptiveSupport private final int[] rendererMixedMimeTypeAdaptiveSupports;
@ -99,6 +100,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
private final TrackGroupArray unmappedTrackGroups;
/**
* @param rendererNames The name of each renderer.
* @param rendererTrackTypes The track type handled by each renderer.
* @param rendererTrackGroups The {@link TrackGroup}s mapped to each renderer.
* @param rendererMixedMimeTypeAdaptiveSupports The {@link AdaptiveSupport} for mixed MIME type
@ -109,11 +111,13 @@ public abstract class MappingTrackSelector extends TrackSelector {
*/
@SuppressWarnings("deprecation")
/* package */ MappedTrackInfo(
String[] rendererNames,
int[] rendererTrackTypes,
TrackGroupArray[] rendererTrackGroups,
@AdaptiveSupport int[] rendererMixedMimeTypeAdaptiveSupports,
@Capabilities int[][][] rendererFormatSupports,
TrackGroupArray unmappedTrackGroups) {
this.rendererNames = rendererNames;
this.rendererTrackTypes = rendererTrackTypes;
this.rendererTrackGroups = rendererTrackGroups;
this.rendererFormatSupports = rendererFormatSupports;
@ -128,6 +132,17 @@ public abstract class MappingTrackSelector extends TrackSelector {
return rendererCount;
}
/**
* Returns the name of the renderer at a given index.
*
* @see Renderer#getName()
* @param rendererIndex The renderer index.
* @return The name of the renderer.
*/
public String getRendererName(int rendererIndex) {
return rendererNames[rendererIndex];
}
/**
* Returns the track type that the renderer at a given index handles.
*
@ -380,6 +395,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
// Create a track group array for each renderer, and trim each rendererFormatSupports entry.
TrackGroupArray[] rendererTrackGroupArrays = new TrackGroupArray[rendererCapabilities.length];
String[] rendererNames = new String[rendererCapabilities.length];
int[] rendererTrackTypes = new int[rendererCapabilities.length];
for (int i = 0; i < rendererCapabilities.length; i++) {
int rendererTrackGroupCount = rendererTrackGroupCounts[i];
@ -388,6 +404,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
Util.nullSafeArrayCopy(rendererTrackGroups[i], rendererTrackGroupCount));
rendererFormatSupports[i] =
Util.nullSafeArrayCopy(rendererFormatSupports[i], rendererTrackGroupCount);
rendererNames[i] = rendererCapabilities[i].getName();
rendererTrackTypes[i] = rendererCapabilities[i].getTrackType();
}
@ -401,6 +418,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
// Package up the track information and selections.
MappedTrackInfo mappedTrackInfo =
new MappedTrackInfo(
rendererNames,
rendererTrackTypes,
rendererTrackGroupArrays,
rendererMixedMimeTypeAdaptationSupports,

View File

@ -216,8 +216,10 @@ public class EventLogger implements AnalyticsListener {
for (int rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) {
TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
TrackSelection trackSelection = trackSelections.get(rendererIndex);
if (rendererTrackGroups.length > 0) {
logd(" Renderer:" + rendererIndex + " [");
if (rendererTrackGroups.length == 0) {
logd(" " + mappedTrackInfo.getRendererName(rendererIndex) + " []");
} else {
logd(" " + mappedTrackInfo.getRendererName(rendererIndex) + " [");
for (int groupIndex = 0; groupIndex < rendererTrackGroups.length; groupIndex++) {
TrackGroup trackGroup = rendererTrackGroups.get(groupIndex);
String adaptiveSupport =
@ -261,7 +263,7 @@ public class EventLogger implements AnalyticsListener {
// Log tracks not associated with a renderer.
TrackGroupArray unassociatedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups();
if (unassociatedTrackGroups.length > 0) {
logd(" Renderer:None [");
logd(" Unmapped [");
for (int groupIndex = 0; groupIndex < unassociatedTrackGroups.length; groupIndex++) {
logd(" Group:" + groupIndex + " [");
TrackGroup trackGroup = unassociatedTrackGroups.get(groupIndex);

View File

@ -240,11 +240,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
clearReportedVideoSize();
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
protected int supportsFormat(
MediaCodecSelector mediaCodecSelector,
Format format)
protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
throws DecoderQueryException {
String mimeType = format.sampleMimeType;
if (!MimeTypes.isVideo(mimeType)) {

View File

@ -33,6 +33,7 @@ import java.nio.ByteBuffer;
/** A {@link Renderer} that parses the camera motion track. */
public class CameraMotionRenderer extends BaseRenderer {
private static final String TAG = "CameraMotionRenderer";
// The amount of time to read samples ahead of the current time.
private static final int SAMPLE_WINDOW_DURATION_US = 100000;
@ -49,6 +50,11 @@ public class CameraMotionRenderer extends BaseRenderer {
scratch = new ParsableByteArray();
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public int supportsFormat(Format format) {

View File

@ -58,6 +58,11 @@ public class DecoderAudioRendererTest {
MockitoAnnotations.initMocks(this);
audioRenderer =
new DecoderAudioRenderer(null, null, mockAudioSink) {
@Override
public String getName() {
return "TestAudioRenderer";
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {

View File

@ -49,6 +49,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.Selecti
import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
@ -1546,6 +1547,11 @@ public final class DefaultTrackSelectorTest {
this.supportValue = supportValue;
}
@Override
public String getName() {
return "FakeRenderer(" + Util.getTrackTypeString(trackType) + ")";
}
@Override
public int getTrackType() {
return trackType;
@ -1590,6 +1596,11 @@ public final class DefaultTrackSelectorTest {
this.formatToCapability = new HashMap<>(formatToCapability);
}
@Override
public String getName() {
return "FakeRenderer(" + Util.getTrackTypeString(trackType) + ")";
}
@Override
public int getTrackType() {
return trackType;

View File

@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -169,6 +170,11 @@ public final class MappingTrackSelectorTest {
this.trackType = trackType;
}
@Override
public String getName() {
return "FakeRenderer(" + Util.getTrackTypeString(trackType) + ")";
}
@Override
public int getTrackType() {
return trackType;

View File

@ -80,6 +80,11 @@ public final class DecoderVideoRendererTest {
@C.VideoOutputMode private int outputMode;
@Override
public String getName() {
return "TestVideoRenderer";
}
@Override
@Capabilities
public int supportsFormat(Format format) {

View File

@ -72,6 +72,7 @@ import java.util.ArrayList;
*/
private static class DebugMediaCodecVideoRenderer extends MediaCodecVideoRenderer {
private static final String TAG = "DebugMediaCodecVideoRenderer";
private static final int ARRAY_SIZE = 1000;
private final long[] timestampsList = new long[ARRAY_SIZE];
@ -98,6 +99,11 @@ import java.util.ArrayList;
maxDroppedFrameCountToNotify);
}
@Override
public String getName() {
return TAG;
}
@Override
protected void configureCodec(
MediaCodecInfo codecInfo,

View File

@ -39,6 +39,7 @@ import java.util.List;
*/
public class FakeRenderer extends BaseRenderer {
private static final String TAG = "FakeRenderer";
/**
* The amount of time ahead of the current playback position that the renderer reads from the
* source. A real renderer will typically read ahead by a small amount due to pipelining through
@ -64,6 +65,11 @@ public class FakeRenderer extends BaseRenderer {
formatsRead = new ArrayList<>();
}
@Override
public String getName() {
return TAG;
}
@Override
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
playbackPositionUs = positionUs;
@ -93,6 +99,7 @@ public class FakeRenderer extends BaseRenderer {
Util.formatInvariant(
"Format track type (%s) doesn't match renderer track type (%s).",
MimeTypes.getTrackType(format.sampleMimeType), getTrackType())),
getName(),
getIndex(),
format,
FORMAT_UNSUPPORTED_TYPE);