Turn on nullness checker for playback stats

The nullness checker complains about Integers with @IntDef annotations
so replace pairs with custom pair types for the timed event records in
PlaybackStats.

PiperOrigin-RevId: 284731834
This commit is contained in:
andrewlewis 2019-12-10 10:48:14 +00:00 committed by Oliver Woodman
parent 70ba4b197c
commit c027b4e71a
2 changed files with 166 additions and 40 deletions

View file

@ -16,8 +16,8 @@
package com.google.android.exoplayer2.analytics;
import android.os.SystemClock;
import android.util.Pair;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime;
@ -28,11 +28,136 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collections;
import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Statistics about playbacks. */
public final class PlaybackStats {
/** Stores a playback state with the event time at which it became active. */
public static final class EventTimeAndPlaybackState {
/** The event time at which the playback state became active. */
public final EventTime eventTime;
/** The playback state that became active. */
public final @PlaybackState int playbackState;
/**
* Creates a new timed playback state event.
*
* @param eventTime The event time at which the playback state became active.
* @param playbackState The playback state that became active.
*/
public EventTimeAndPlaybackState(EventTime eventTime, @PlaybackState int playbackState) {
this.eventTime = eventTime;
this.playbackState = playbackState;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EventTimeAndPlaybackState that = (EventTimeAndPlaybackState) o;
if (playbackState != that.playbackState) {
return false;
}
return eventTime.equals(that.eventTime);
}
@Override
public int hashCode() {
int result = eventTime.hashCode();
result = 31 * result + playbackState;
return result;
}
}
/**
* Stores a format with the event time at which it started being used, or {@code null} to indicate
* that no format was used.
*/
public static final class EventTimeAndFormat {
/** The event time associated with {@link #format}. */
public final EventTime eventTime;
/** The format that started being used, or {@code null} if no format was used. */
@Nullable public final Format format;
/**
* Creates a new timed format event.
*
* @param eventTime The event time associated with {@code format}.
* @param format The format that started being used, or {@code null} if no format was used.
*/
public EventTimeAndFormat(EventTime eventTime, @Nullable Format format) {
this.eventTime = eventTime;
this.format = format;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EventTimeAndFormat that = (EventTimeAndFormat) o;
if (!eventTime.equals(that.eventTime)) {
return false;
}
return format != null ? format.equals(that.format) : that.format == null;
}
@Override
public int hashCode() {
int result = eventTime.hashCode();
result = 31 * result + (format != null ? format.hashCode() : 0);
return result;
}
}
/** Stores an exception with the event time at which it occurred. */
public static final class EventTimeAndException {
/** The event time at which the exception occurred. */
public final EventTime eventTime;
/** The exception that was thrown. */
public final Exception exception;
/**
* Creates a new timed exception event.
*
* @param eventTime The event time at which the exception occurred.
* @param exception The exception that was thrown.
*/
public EventTimeAndException(EventTime eventTime, Exception exception) {
this.eventTime = eventTime;
this.exception = exception;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EventTimeAndException that = (EventTimeAndException) o;
if (!eventTime.equals(that.eventTime)) {
return false;
}
return exception.equals(that.exception);
}
@Override
public int hashCode() {
int result = eventTime.hashCode();
result = 31 * result + exception.hashCode();
return result;
}
}
/**
* State of a playback. One of {@link #PLAYBACK_STATE_NOT_STARTED}, {@link
* #PLAYBACK_STATE_JOINING_FOREGROUND}, {@link #PLAYBACK_STATE_JOINING_BACKGROUND}, {@link
@ -258,10 +383,10 @@ public final class PlaybackStats {
// Playback state stats.
/**
* The playback state history as ordered pairs of the {@link EventTime} at which a state became
* active and the {@link PlaybackState}.
* The playback state history as {@link EventTimeAndPlaybackState EventTimeAndPlaybackStates}
* ordered by {@code EventTime.realTimeMs}.
*/
public final List<Pair<EventTime, @PlaybackState Integer>> playbackStateHistory;
public final List<EventTimeAndPlaybackState> playbackStateHistory;
/**
* The media time history as an ordered list of long[2] arrays with [0] being the realtime as
* returned by {@code SystemClock.elapsedRealtime()} and [1] being the media time at this
@ -319,15 +444,15 @@ public final class PlaybackStats {
// Format stats.
/**
* The video format history as ordered pairs of the {@link EventTime} at which a format started
* being used and the {@link Format}. The {@link Format} may be null if no video format was used.
* The video format history as {@link EventTimeAndFormat EventTimeAndFormats} ordered by {@code
* EventTime.realTimeMs}. The {@link Format} may be null if no video format was used.
*/
public final List<Pair<EventTime, @NullableType Format>> videoFormatHistory;
public final List<EventTimeAndFormat> videoFormatHistory;
/**
* The audio format history as ordered pairs of the {@link EventTime} at which a format started
* being used and the {@link Format}. The {@link Format} may be null if no audio format was used.
* The audio format history as {@link EventTimeAndFormat EventTimeAndFormats} ordered by {@code
* EventTime.realTimeMs}. The {@link Format} may be null if no audio format was used.
*/
public final List<Pair<EventTime, @NullableType Format>> audioFormatHistory;
public final List<EventTimeAndFormat> audioFormatHistory;
/** The total media time for which video format height data is available, in milliseconds. */
public final long totalVideoFormatHeightTimeMs;
/**
@ -400,23 +525,23 @@ public final class PlaybackStats {
*/
public final int nonFatalErrorCount;
/**
* The history of fatal errors as ordered pairs of the {@link EventTime} at which an error
* occurred and the error. Errors are fatal if playback stopped due to this error.
* The history of fatal errors as {@link EventTimeAndException EventTimeAndExceptions} ordered by
* {@code EventTime.realTimeMs}. Errors are fatal if playback stopped due to this error.
*/
public final List<Pair<EventTime, Exception>> fatalErrorHistory;
public final List<EventTimeAndException> fatalErrorHistory;
/**
* The history of non-fatal errors as ordered pairs of the {@link EventTime} at which an error
* occurred and the error. Error are non-fatal if playback can recover from the error without
* stopping.
* The history of non-fatal errors as {@link EventTimeAndException EventTimeAndExceptions} ordered
* by {@code EventTime.realTimeMs}. Errors are non-fatal if playback can recover from the error
* without stopping.
*/
public final List<Pair<EventTime, Exception>> nonFatalErrorHistory;
public final List<EventTimeAndException> nonFatalErrorHistory;
private final long[] playbackStateDurationsMs;
/* package */ PlaybackStats(
int playbackCount,
long[] playbackStateDurationsMs,
List<Pair<EventTime, @PlaybackState Integer>> playbackStateHistory,
List<EventTimeAndPlaybackState> playbackStateHistory,
List<long[]> mediaTimeHistory,
long firstReportedTimeMs,
int foregroundPlaybackCount,
@ -431,8 +556,8 @@ public final class PlaybackStats {
int totalRebufferCount,
long maxRebufferTimeMs,
int adPlaybackCount,
List<Pair<EventTime, @NullableType Format>> videoFormatHistory,
List<Pair<EventTime, @NullableType Format>> audioFormatHistory,
List<EventTimeAndFormat> videoFormatHistory,
List<EventTimeAndFormat> audioFormatHistory,
long totalVideoFormatHeightTimeMs,
long totalVideoFormatHeightTimeProduct,
long totalVideoFormatBitrateTimeMs,
@ -452,8 +577,8 @@ public final class PlaybackStats {
int fatalErrorPlaybackCount,
int fatalErrorCount,
int nonFatalErrorCount,
List<Pair<EventTime, Exception>> fatalErrorHistory,
List<Pair<EventTime, Exception>> nonFatalErrorHistory) {
List<EventTimeAndException> fatalErrorHistory,
List<EventTimeAndException> nonFatalErrorHistory) {
this.playbackCount = playbackCount;
this.playbackStateDurationsMs = playbackStateDurationsMs;
this.playbackStateHistory = Collections.unmodifiableList(playbackStateHistory);
@ -515,11 +640,11 @@ public final class PlaybackStats {
*/
public @PlaybackState int getPlaybackStateAtTime(long realtimeMs) {
@PlaybackState int state = PLAYBACK_STATE_NOT_STARTED;
for (Pair<EventTime, @PlaybackState Integer> timeAndState : playbackStateHistory) {
if (timeAndState.first.realtimeMs > realtimeMs) {
for (EventTimeAndPlaybackState timeAndState : playbackStateHistory) {
if (timeAndState.eventTime.realtimeMs > realtimeMs) {
break;
}
state = timeAndState.second;
state = timeAndState.playbackState;
}
return state;
}

View file

@ -16,7 +16,6 @@
package com.google.android.exoplayer2.analytics;
import android.os.SystemClock;
import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
@ -25,6 +24,9 @@ import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.analytics.PlaybackStats.EventTimeAndException;
import com.google.android.exoplayer2.analytics.PlaybackStats.EventTimeAndFormat;
import com.google.android.exoplayer2.analytics.PlaybackStats.EventTimeAndPlaybackState;
import com.google.android.exoplayer2.analytics.PlaybackStats.PlaybackState;
import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaLoadData;
@ -42,7 +44,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/**
* {@link AnalyticsListener} to gather {@link PlaybackStats} from the player.
@ -433,12 +434,12 @@ public final class PlaybackStatsListener
// Final stats.
private final boolean keepHistory;
private final long[] playbackStateDurationsMs;
private final List<Pair<EventTime, @PlaybackState Integer>> playbackStateHistory;
private final List<EventTimeAndPlaybackState> playbackStateHistory;
private final List<long[]> mediaTimeHistory;
private final List<Pair<EventTime, @NullableType Format>> videoFormatHistory;
private final List<Pair<EventTime, @NullableType Format>> audioFormatHistory;
private final List<Pair<EventTime, Exception>> fatalErrorHistory;
private final List<Pair<EventTime, Exception>> nonFatalErrorHistory;
private final List<EventTimeAndFormat> videoFormatHistory;
private final List<EventTimeAndFormat> audioFormatHistory;
private final List<EventTimeAndException> fatalErrorHistory;
private final List<EventTimeAndException> nonFatalErrorHistory;
private final boolean isAd;
private long firstReportedTimeMs;
@ -589,7 +590,7 @@ public final class PlaybackStatsListener
public void onFatalError(EventTime eventTime, Exception error) {
fatalErrorCount++;
if (keepHistory) {
fatalErrorHistory.add(Pair.create(eventTime, error));
fatalErrorHistory.add(new EventTimeAndException(eventTime, error));
}
hasFatalError = true;
isInterruptedByAd = false;
@ -743,7 +744,7 @@ public final class PlaybackStatsListener
public void onNonFatalError(EventTime eventTime, Exception error) {
nonFatalErrorCount++;
if (keepHistory) {
nonFatalErrorHistory.add(Pair.create(eventTime, error));
nonFatalErrorHistory.add(new EventTimeAndException(eventTime, error));
}
}
@ -776,9 +777,9 @@ public final class PlaybackStatsListener
: playbackStateDurationsMs[PlaybackStats.PLAYBACK_STATE_JOINING_FOREGROUND];
boolean hasBackgroundJoin =
playbackStateDurationsMs[PlaybackStats.PLAYBACK_STATE_JOINING_BACKGROUND] > 0;
List<Pair<EventTime, @NullableType Format>> videoHistory =
List<EventTimeAndFormat> videoHistory =
isFinal ? videoFormatHistory : new ArrayList<>(videoFormatHistory);
List<Pair<EventTime, @NullableType Format>> audioHistory =
List<EventTimeAndFormat> audioHistory =
isFinal ? audioFormatHistory : new ArrayList<>(audioFormatHistory);
return new PlaybackStats(
/* playbackCount= */ 1,
@ -864,7 +865,7 @@ public final class PlaybackStatsListener
currentPlaybackState = newPlaybackState;
currentPlaybackStateStartTimeMs = eventTime.realtimeMs;
if (keepHistory) {
playbackStateHistory.add(Pair.create(eventTime, currentPlaybackState));
playbackStateHistory.add(new EventTimeAndPlaybackState(eventTime, currentPlaybackState));
}
}
@ -973,7 +974,7 @@ public final class PlaybackStatsListener
}
currentVideoFormat = newFormat;
if (keepHistory) {
videoFormatHistory.add(Pair.create(eventTime, currentVideoFormat));
videoFormatHistory.add(new EventTimeAndFormat(eventTime, currentVideoFormat));
}
}
@ -989,7 +990,7 @@ public final class PlaybackStatsListener
}
currentAudioFormat = newFormat;
if (keepHistory) {
audioFormatHistory.add(Pair.create(eventTime, currentAudioFormat));
audioFormatHistory.add(new EventTimeAndFormat(eventTime, currentAudioFormat));
}
}