Commit dc33c0bd by olly Committed by Oliver Woodman

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
parent adae53c7
Showing with 224 additions and 44 deletions
......@@ -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. */
......@@ -107,6 +108,11 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public final int supportsFormat(Format format) {
if (!MimeTypes.VIDEO_AV1.equalsIgnoreCase(format.sampleMimeType)
......
......@@ -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. */
......@@ -89,6 +91,11 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {
String mimeType = Assertions.checkNotNull(format.sampleMimeType);
......
......@@ -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.
*
......@@ -57,6 +59,11 @@ public final class FfmpegVideoRenderer extends DecoderVideoRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
@RendererCapabilities.Capabilities
public final int supportsFormat(Format format) {
// TODO: Remove this line and uncomment the implementation below.
......
......@@ -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;
......@@ -72,6 +73,11 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {
if (!FlacLibrary.isAvailable()
......
......@@ -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. */
......@@ -54,6 +55,11 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {
boolean drmIsSupported =
......
......@@ -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;
/**
......@@ -116,6 +118,11 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public final int supportsFormat(Format format) {
if (!VpxLibrary.isAvailable() || !MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)) {
......
......@@ -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);
}
/**
......
......@@ -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;
}
}
......@@ -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.
*
......
......@@ -190,6 +190,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
* renderer will return {@link C#TRACK_TYPE_TEXT}, and so on.
......
......@@ -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
......
......@@ -202,10 +202,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
@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)) {
......
......@@ -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
......@@ -93,6 +94,11 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public int supportsFormat(Format format) {
if (decoderFactory.supportsFormat(format)) {
......
......@@ -122,6 +122,11 @@ public final class TextRenderer extends BaseRenderer implements Callback {
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public int supportsFormat(Format format) {
if (decoderFactory.supportsFormat(format)) {
......
......@@ -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;
......@@ -129,6 +133,17 @@ public abstract class MappingTrackSelector extends TrackSelector {
}
/**
* 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.
*
* @see Renderer#getTrackType()
......@@ -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,
......
......@@ -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);
......
......@@ -241,10 +241,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
@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)) {
......
......@@ -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;
......@@ -50,6 +51,11 @@ public class CameraMotionRenderer extends BaseRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
@Capabilities
public int supportsFormat(Format format) {
return MimeTypes.APPLICATION_CAMERA_MOTION.equals(format.sampleMimeType)
......
......@@ -59,6 +59,11 @@ public class DecoderAudioRendererTest {
audioRenderer =
new DecoderAudioRenderer(null, null, mockAudioSink) {
@Override
public String getName() {
return "TestAudioRenderer";
}
@Override
@FormatSupport
protected int supportsFormatInternal(Format format) {
return FORMAT_HANDLED;
......
......@@ -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;
......@@ -1547,6 +1548,11 @@ public final class DefaultTrackSelectorTest {
}
@Override
public String getName() {
return "FakeRenderer(" + Util.getTrackTypeString(trackType) + ")";
}
@Override
public int getTrackType() {
return trackType;
}
......@@ -1591,6 +1597,11 @@ public final class DefaultTrackSelectorTest {
}
@Override
public String getName() {
return "FakeRenderer(" + Util.getTrackTypeString(trackType) + ")";
}
@Override
public int getTrackType() {
return trackType;
}
......
......@@ -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;
......@@ -170,6 +171,11 @@ public final class MappingTrackSelectorTest {
}
@Override
public String getName() {
return "FakeRenderer(" + Util.getTrackTypeString(trackType) + ")";
}
@Override
public int getTrackType() {
return trackType;
}
......
......@@ -81,6 +81,11 @@ public final class DecoderVideoRendererTest {
@C.VideoOutputMode private int outputMode;
@Override
public String getName() {
return "TestVideoRenderer";
}
@Override
@Capabilities
public int supportsFormat(Format format) {
return RendererCapabilities.create(FORMAT_HANDLED);
......
......@@ -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];
......@@ -99,6 +100,11 @@ import java.util.ArrayList;
}
@Override
public String getName() {
return TAG;
}
@Override
protected void configureCodec(
MediaCodecInfo codecInfo,
MediaCodec codec,
......
......@@ -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
......@@ -65,6 +66,11 @@ public class FakeRenderer extends BaseRenderer {
}
@Override
public String getName() {
return TAG;
}
@Override
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
playbackPositionUs = positionUs;
lastSamplePositionUs = Long.MIN_VALUE;
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment