Add missing events to AnalyticsListener.
And also add a test that all Player.Listener events are forwarded to AnalyticsListener. The AnalyticsCollector also needlessly implemented Video/AudioRendererEventListener, which is not needed because all of the equivalent methods are called directly and never through the interface. #minor-release PiperOrigin-RevId: 427478000
This commit is contained in:
parent
018631320b
commit
c3cb2f7cfb
@ -106,12 +106,12 @@ public class ForwardingPlayerTest {
|
|||||||
public void forwardingPlayer_overridesAllPlayerMethods() throws Exception {
|
public void forwardingPlayer_overridesAllPlayerMethods() throws Exception {
|
||||||
// Check with reflection that ForwardingPlayer overrides all Player methods.
|
// Check with reflection that ForwardingPlayer overrides all Player methods.
|
||||||
List<Method> methods = getPublicMethods(Player.class);
|
List<Method> methods = getPublicMethods(Player.class);
|
||||||
for (int i = 0; i < methods.size(); i++) {
|
for (Method method : methods) {
|
||||||
Method method = methods.get(i);
|
|
||||||
assertThat(
|
assertThat(
|
||||||
ForwardingPlayer.class.getDeclaredMethod(
|
ForwardingPlayer.class
|
||||||
method.getName(), method.getParameterTypes()))
|
.getDeclaredMethod(method.getName(), method.getParameterTypes())
|
||||||
.isNotNull();
|
.getDeclaringClass())
|
||||||
|
.isEqualTo(ForwardingPlayer.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,10 +120,12 @@ public class ForwardingPlayerTest {
|
|||||||
// Check with reflection that ForwardingListener overrides all Listener methods.
|
// Check with reflection that ForwardingListener overrides all Listener methods.
|
||||||
Class<?> forwardingListenerClass = getInnerClass("ForwardingListener");
|
Class<?> forwardingListenerClass = getInnerClass("ForwardingListener");
|
||||||
List<Method> methods = getPublicMethods(Player.Listener.class);
|
List<Method> methods = getPublicMethods(Player.Listener.class);
|
||||||
for (int i = 0; i < methods.size(); i++) {
|
for (Method method : methods) {
|
||||||
Method method = methods.get(i);
|
assertThat(
|
||||||
assertThat(forwardingListenerClass.getMethod(method.getName(), method.getParameterTypes()))
|
forwardingListenerClass
|
||||||
.isNotNull();
|
.getMethod(method.getName(), method.getParameterTypes())
|
||||||
|
.getDeclaringClass())
|
||||||
|
.isEqualTo(forwardingListenerClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,18 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
|
|||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
|
import android.media.AudioTrack;
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.media.MediaCodec.CodecException;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
import android.view.Surface;
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.AudioAttributes;
|
import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.DeviceInfo;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
@ -32,24 +38,28 @@ import androidx.media3.common.Metadata;
|
|||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
import androidx.media3.common.PlaybackParameters;
|
import androidx.media3.common.PlaybackParameters;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
|
import androidx.media3.common.Player.DiscontinuityReason;
|
||||||
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.Timeline.Period;
|
import androidx.media3.common.Timeline.Period;
|
||||||
import androidx.media3.common.Timeline.Window;
|
import androidx.media3.common.Timeline.Window;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
import androidx.media3.common.TrackGroupArray;
|
||||||
import androidx.media3.common.TrackSelectionArray;
|
import androidx.media3.common.TrackSelectionArray;
|
||||||
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
|
import androidx.media3.common.text.Cue;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.HandlerWrapper;
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.ListenerSet;
|
import androidx.media3.common.util.ListenerSet;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.decoder.DecoderException;
|
||||||
import androidx.media3.exoplayer.DecoderCounters;
|
import androidx.media3.exoplayer.DecoderCounters;
|
||||||
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime;
|
import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime;
|
||||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.drm.DrmSession;
|
import androidx.media3.exoplayer.drm.DrmSession;
|
||||||
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||||
import androidx.media3.exoplayer.source.LoadEventInfo;
|
import androidx.media3.exoplayer.source.LoadEventInfo;
|
||||||
@ -57,7 +67,7 @@ import androidx.media3.exoplayer.source.MediaLoadData;
|
|||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
import androidx.media3.exoplayer.video.VideoRendererEventListener;
|
import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
@ -73,8 +83,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class AnalyticsCollector
|
public class AnalyticsCollector
|
||||||
implements Player.Listener,
|
implements Player.Listener,
|
||||||
AudioRendererEventListener,
|
|
||||||
VideoRendererEventListener,
|
|
||||||
MediaSourceEventListener,
|
MediaSourceEventListener,
|
||||||
BandwidthMeter.EventListener,
|
BandwidthMeter.EventListener,
|
||||||
DrmSessionEventListener {
|
DrmSessionEventListener {
|
||||||
@ -185,10 +193,15 @@ public class AnalyticsCollector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudioRendererEventListener implementation.
|
// Audio events.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the audio renderer is enabled.
|
||||||
|
*
|
||||||
|
* @param counters {@link DecoderCounters} that will be updated by the audio renderer for as long
|
||||||
|
* as it remains enabled.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onAudioEnabled(DecoderCounters counters) {
|
public final void onAudioEnabled(DecoderCounters counters) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -200,8 +213,15 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a audio decoder is created.
|
||||||
|
*
|
||||||
|
* @param decoderName The audio decoder that was created.
|
||||||
|
* @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization
|
||||||
|
* finished.
|
||||||
|
* @param initializationDurationMs The time taken to initialize the decoder in milliseconds.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onAudioDecoderInitialized(
|
public final void onAudioDecoderInitialized(
|
||||||
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
|
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
@ -217,8 +237,15 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the format of the media being consumed by the audio renderer changes.
|
||||||
|
*
|
||||||
|
* @param format The new format.
|
||||||
|
* @param decoderReuseEvaluation The result of the evaluation to determine whether an existing
|
||||||
|
* decoder instance can be reused for the new format, or {@code null} if the renderer did not
|
||||||
|
* have a decoder.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onAudioInputFormatChanged(
|
public final void onAudioInputFormatChanged(
|
||||||
Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {
|
Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
@ -232,7 +259,13 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called when the audio position has increased for the first time since the last pause or
|
||||||
|
* position reset.
|
||||||
|
*
|
||||||
|
* @param playoutStartSystemTimeMs The approximate derived {@link System#currentTimeMillis()} at
|
||||||
|
* which playout started.
|
||||||
|
*/
|
||||||
public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) {
|
public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -241,7 +274,14 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs));
|
listener -> listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called when an audio underrun occurs.
|
||||||
|
*
|
||||||
|
* @param bufferSize The size of the audio output buffer, in bytes.
|
||||||
|
* @param bufferSizeMs The size of the audio output buffer, in milliseconds, if it contains PCM
|
||||||
|
* encoded audio. {@link C#TIME_UNSET} if the output buffer contains non-PCM encoded audio.
|
||||||
|
* @param elapsedSinceLastFeedMs The time since audio was last written to the output buffer.
|
||||||
|
*/
|
||||||
public final void onAudioUnderrun(
|
public final void onAudioUnderrun(
|
||||||
int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
@ -252,7 +292,11 @@ public class AnalyticsCollector
|
|||||||
listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs));
|
listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called when a audio decoder is released.
|
||||||
|
*
|
||||||
|
* @param decoderName The audio decoder that was released.
|
||||||
|
*/
|
||||||
public final void onAudioDecoderReleased(String decoderName) {
|
public final void onAudioDecoderReleased(String decoderName) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -261,8 +305,12 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onAudioDecoderReleased(eventTime, decoderName));
|
listener -> listener.onAudioDecoderReleased(eventTime, decoderName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the audio renderer is disabled.
|
||||||
|
*
|
||||||
|
* @param counters {@link DecoderCounters} that were updated by the audio renderer.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onAudioDisabled(DecoderCounters counters) {
|
public final void onAudioDisabled(DecoderCounters counters) {
|
||||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -274,16 +322,16 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public final void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
|
* Called when {@link AudioSink} has encountered an error.
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
*
|
||||||
sendEvent(
|
* <p>If the sink writes to a platform {@link AudioTrack}, this will be called for all {@link
|
||||||
eventTime,
|
* AudioTrack} errors.
|
||||||
AnalyticsListener.EVENT_SKIP_SILENCE_ENABLED_CHANGED,
|
*
|
||||||
listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled));
|
* @param audioSinkError The error that occurred. Typically an {@link
|
||||||
}
|
* AudioSink.InitializationException}, a {@link AudioSink.WriteException}, or an {@link
|
||||||
|
* AudioSink.UnexpectedDiscontinuityException}.
|
||||||
@Override
|
*/
|
||||||
public final void onAudioSinkError(Exception audioSinkError) {
|
public final void onAudioSinkError(Exception audioSinkError) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -292,7 +340,12 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onAudioSinkError(eventTime, audioSinkError));
|
listener -> listener.onAudioSinkError(eventTime, audioSinkError));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called when an audio decoder encounters an error.
|
||||||
|
*
|
||||||
|
* @param audioCodecError The error. Typically a {@link CodecException} if the renderer uses
|
||||||
|
* {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder.
|
||||||
|
*/
|
||||||
public final void onAudioCodecError(Exception audioCodecError) {
|
public final void onAudioCodecError(Exception audioCodecError) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -301,34 +354,6 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onAudioCodecError(eventTime, audioCodecError));
|
listener -> listener.onAudioCodecError(eventTime, audioCodecError));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional audio events.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the audio session ID changes.
|
|
||||||
*
|
|
||||||
* @param audioSessionId The audio session ID.
|
|
||||||
*/
|
|
||||||
public final void onAudioSessionIdChanged(int audioSessionId) {
|
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
|
||||||
sendEvent(
|
|
||||||
eventTime,
|
|
||||||
AnalyticsListener.EVENT_AUDIO_SESSION_ID,
|
|
||||||
listener -> listener.onAudioSessionIdChanged(eventTime, audioSessionId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the audio attributes change.
|
|
||||||
*
|
|
||||||
* @param audioAttributes The audio attributes.
|
|
||||||
*/
|
|
||||||
public final void onAudioAttributesChanged(AudioAttributes audioAttributes) {
|
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
|
||||||
sendEvent(
|
|
||||||
eventTime,
|
|
||||||
AnalyticsListener.EVENT_AUDIO_ATTRIBUTES_CHANGED,
|
|
||||||
listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the volume changes.
|
* Called when the volume changes.
|
||||||
*
|
*
|
||||||
@ -342,10 +367,15 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onVolumeChanged(eventTime, volume));
|
listener -> listener.onVolumeChanged(eventTime, volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoRendererEventListener implementation.
|
// Video events.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the video renderer is enabled.
|
||||||
|
*
|
||||||
|
* @param counters {@link DecoderCounters} that will be updated by the video renderer for as long
|
||||||
|
* as it remains enabled.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onVideoEnabled(DecoderCounters counters) {
|
public final void onVideoEnabled(DecoderCounters counters) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -357,8 +387,15 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a video decoder is created.
|
||||||
|
*
|
||||||
|
* @param decoderName The decoder that was created.
|
||||||
|
* @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization
|
||||||
|
* finished.
|
||||||
|
* @param initializationDurationMs The time taken to initialize the decoder in milliseconds.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onVideoDecoderInitialized(
|
public final void onVideoDecoderInitialized(
|
||||||
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
|
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
@ -374,8 +411,15 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the format of the media being consumed by the video renderer changes.
|
||||||
|
*
|
||||||
|
* @param format The new format.
|
||||||
|
* @param decoderReuseEvaluation The result of the evaluation to determine whether an existing
|
||||||
|
* decoder instance can be reused for the new format, or {@code null} if the renderer did not
|
||||||
|
* have a decoder.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onVideoInputFormatChanged(
|
public final void onVideoInputFormatChanged(
|
||||||
Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {
|
Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
@ -389,7 +433,16 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called to report the number of frames dropped by the video renderer. Dropped frames are
|
||||||
|
* reported whenever the renderer is stopped having dropped frames, and optionally, whenever the
|
||||||
|
* count reaches a specified threshold whilst the renderer is started.
|
||||||
|
*
|
||||||
|
* @param count The number of dropped frames.
|
||||||
|
* @param elapsedMs The duration in milliseconds over which the frames were dropped. This duration
|
||||||
|
* is timed from when the renderer was started or from when dropped frames were last reported
|
||||||
|
* (whichever was more recent), and not from when the first of the reported drops occurred.
|
||||||
|
*/
|
||||||
public final void onDroppedFrames(int count, long elapsedMs) {
|
public final void onDroppedFrames(int count, long elapsedMs) {
|
||||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -398,7 +451,11 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onDroppedVideoFrames(eventTime, count, elapsedMs));
|
listener -> listener.onDroppedVideoFrames(eventTime, count, elapsedMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called when a video decoder is released.
|
||||||
|
*
|
||||||
|
* @param decoderName The video decoder that was released.
|
||||||
|
*/
|
||||||
public final void onVideoDecoderReleased(String decoderName) {
|
public final void onVideoDecoderReleased(String decoderName) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -407,8 +464,12 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onVideoDecoderReleased(eventTime, decoderName));
|
listener -> listener.onVideoDecoderReleased(eventTime, decoderName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the video renderer is disabled.
|
||||||
|
*
|
||||||
|
* @param counters {@link DecoderCounters} that were updated by the video renderer.
|
||||||
|
*/
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
|
||||||
public final void onVideoDisabled(DecoderCounters counters) {
|
public final void onVideoDisabled(DecoderCounters counters) {
|
||||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -420,25 +481,14 @@ public class AnalyticsCollector
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
/**
|
||||||
@Override
|
* Called when a frame is rendered for the first time since setting the output, or since the
|
||||||
public final void onVideoSizeChanged(VideoSize videoSize) {
|
* renderer was reset, or since the stream being rendered was changed.
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
*
|
||||||
sendEvent(
|
* @param output The output of the video renderer. Normally a {@link Surface}, however some video
|
||||||
eventTime,
|
* renderers may have other output types (e.g., a {@link VideoDecoderOutputBufferRenderer}).
|
||||||
AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED,
|
* @param renderTimeMs The {@link SystemClock#elapsedRealtime()} when the frame was rendered.
|
||||||
listener -> {
|
*/
|
||||||
listener.onVideoSizeChanged(eventTime, videoSize);
|
|
||||||
listener.onVideoSizeChanged(
|
|
||||||
eventTime,
|
|
||||||
videoSize.width,
|
|
||||||
videoSize.height,
|
|
||||||
videoSize.unappliedRotationDegrees,
|
|
||||||
videoSize.pixelWidthHeightRatio);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void onRenderedFirstFrame(Object output, long renderTimeMs) {
|
public final void onRenderedFirstFrame(Object output, long renderTimeMs) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -447,7 +497,24 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onRenderedFirstFrame(eventTime, output, renderTimeMs));
|
listener -> listener.onRenderedFirstFrame(eventTime, output, renderTimeMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called to report the video processing offset of video frames processed by the video renderer.
|
||||||
|
*
|
||||||
|
* <p>Video processing offset represents how early a video frame is processed compared to the
|
||||||
|
* player's current position. For each video frame, the offset is calculated as <em>P<sub>vf</sub>
|
||||||
|
* - P<sub>pl</sub></em> where <em>P<sub>vf</sub></em> is the presentation timestamp of the video
|
||||||
|
* frame and <em>P<sub>pl</sub></em> is the current position of the player. Positive values
|
||||||
|
* indicate the frame was processed early enough whereas negative values indicate that the
|
||||||
|
* player's position had progressed beyond the frame's timestamp when the frame was processed (and
|
||||||
|
* the frame was probably dropped).
|
||||||
|
*
|
||||||
|
* <p>The renderer reports the sum of video processing offset samples (one sample per processed
|
||||||
|
* video frame: dropped, skipped or rendered) and the total number of samples.
|
||||||
|
*
|
||||||
|
* @param totalProcessingOffsetUs The sum of all video frame processing offset samples for the
|
||||||
|
* video frames processed by the renderer in microseconds.
|
||||||
|
* @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}.
|
||||||
|
*/
|
||||||
public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
|
public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
|
||||||
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
EventTime eventTime = generatePlayingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -457,7 +524,19 @@ public class AnalyticsCollector
|
|||||||
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount));
|
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Called when a video decoder encounters an error.
|
||||||
|
*
|
||||||
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
* The player may be able to recover from the error. Hence applications should <em>not</em>
|
||||||
|
* implement this method to display a user visible error or initiate an application level retry.
|
||||||
|
* {@link Player.Listener#onPlayerError} is the appropriate place to implement such behavior. This
|
||||||
|
* method is called to provide the application with an opportunity to log the error if it wishes
|
||||||
|
* to do so.
|
||||||
|
*
|
||||||
|
* @param videoCodecError The error. Typically a {@link CodecException} if the renderer uses
|
||||||
|
* {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder.
|
||||||
|
*/
|
||||||
public final void onVideoCodecError(Exception videoCodecError) {
|
public final void onVideoCodecError(Exception videoCodecError) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
sendEvent(
|
sendEvent(
|
||||||
@ -466,8 +545,6 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onVideoCodecError(eventTime, videoCodecError));
|
listener -> listener.onVideoCodecError(eventTime, videoCodecError));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional video events.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called each time there's a change in the size of the surface onto which the video is being
|
* Called each time there's a change in the size of the surface onto which the video is being
|
||||||
* rendered.
|
* rendered.
|
||||||
@ -608,6 +685,12 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onTracksInfoChanged(eventTime, tracksInfo));
|
listener -> listener.onTracksInfoChanged(eventTime, tracksInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // Implementing deprecated method.
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Do nothing. Handled by non-deprecated onIsLoadingChanged.
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
@Override
|
@Override
|
||||||
public final void onIsLoadingChanged(boolean isLoading) {
|
public final void onIsLoadingChanged(boolean isLoading) {
|
||||||
@ -699,21 +782,26 @@ public class AnalyticsCollector
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void onPlayerError(PlaybackException error) {
|
public final void onPlayerError(PlaybackException error) {
|
||||||
@Nullable EventTime eventTime = null;
|
EventTime eventTime = getEventTimeForErrorEvent(error);
|
||||||
if (error instanceof ExoPlaybackException) {
|
|
||||||
ExoPlaybackException exoError = (ExoPlaybackException) error;
|
|
||||||
if (exoError.mediaPeriodId != null) {
|
|
||||||
eventTime = generateEventTime(new MediaPeriodId(exoError.mediaPeriodId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (eventTime == null) {
|
|
||||||
eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
|
||||||
}
|
|
||||||
EventTime finalEventTime = eventTime;
|
|
||||||
sendEvent(
|
sendEvent(
|
||||||
eventTime,
|
eventTime,
|
||||||
AnalyticsListener.EVENT_PLAYER_ERROR,
|
AnalyticsListener.EVENT_PLAYER_ERROR,
|
||||||
listener -> listener.onPlayerError(finalEventTime, error));
|
listener -> listener.onPlayerError(eventTime, error));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerErrorChanged(@Nullable PlaybackException error) {
|
||||||
|
EventTime eventTime = getEventTimeForErrorEvent(error);
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_PLAYER_ERROR,
|
||||||
|
listener -> listener.onPlayerErrorChanged(eventTime, error));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // Implementing deprecated method.
|
||||||
|
@Override
|
||||||
|
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
|
||||||
|
// Do nothing. Handled by non-deprecated onPositionDiscontinuity.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calling deprecated callback.
|
// Calling deprecated callback.
|
||||||
@ -801,6 +889,13 @@ public class AnalyticsCollector
|
|||||||
listener -> listener.onMetadata(eventTime, metadata));
|
listener -> listener.onMetadata(eventTime, metadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCues(List<Cue> cues) {
|
||||||
|
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime, AnalyticsListener.EVENT_CUES, listener -> listener.onCues(eventTime, cues));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Implementing and calling deprecated listener method.
|
@SuppressWarnings("deprecation") // Implementing and calling deprecated listener method.
|
||||||
@Override
|
@Override
|
||||||
public final void onSeekProcessed() {
|
public final void onSeekProcessed() {
|
||||||
@ -809,6 +904,89 @@ public class AnalyticsCollector
|
|||||||
eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekProcessed(eventTime));
|
eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekProcessed(eventTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_SKIP_SILENCE_ENABLED_CHANGED,
|
||||||
|
listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onAudioSessionIdChanged(int audioSessionId) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_AUDIO_SESSION_ID,
|
||||||
|
listener -> listener.onAudioSessionIdChanged(eventTime, audioSessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onAudioAttributesChanged(AudioAttributes audioAttributes) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_AUDIO_ATTRIBUTES_CHANGED,
|
||||||
|
listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // Calling deprecated listener method.
|
||||||
|
@Override
|
||||||
|
public final void onVideoSizeChanged(VideoSize videoSize) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED,
|
||||||
|
listener -> {
|
||||||
|
listener.onVideoSizeChanged(eventTime, videoSize);
|
||||||
|
listener.onVideoSizeChanged(
|
||||||
|
eventTime,
|
||||||
|
videoSize.width,
|
||||||
|
videoSize.height,
|
||||||
|
videoSize.unappliedRotationDegrees,
|
||||||
|
videoSize.pixelWidthHeightRatio);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {
|
||||||
|
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED,
|
||||||
|
listener -> listener.onTrackSelectionParametersChanged(eventTime, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceInfoChanged(DeviceInfo deviceInfo) {
|
||||||
|
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_DEVICE_INFO_CHANGED,
|
||||||
|
listener -> listener.onDeviceInfoChanged(eventTime, deviceInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceVolumeChanged(int volume, boolean muted) {
|
||||||
|
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_DEVICE_VOLUME_CHANGED,
|
||||||
|
listener -> listener.onDeviceVolumeChanged(eventTime, volume, muted));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("UngroupedOverloads") // Grouped by interface.
|
||||||
|
@Override
|
||||||
|
public void onRenderedFirstFrame() {
|
||||||
|
// Do nothing. Handled by onRenderedFirstFrame call with additional parameters.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvents(Player player, Player.Events events) {
|
||||||
|
// Do nothing. AnalyticsCollector issues its own onEvents.
|
||||||
|
}
|
||||||
|
|
||||||
// BandwidthMeter.EventListener implementation.
|
// BandwidthMeter.EventListener implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1002,6 +1180,16 @@ public class AnalyticsCollector
|
|||||||
windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null);
|
windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EventTime getEventTimeForErrorEvent(@Nullable PlaybackException error) {
|
||||||
|
if (error instanceof ExoPlaybackException) {
|
||||||
|
ExoPlaybackException exoError = (ExoPlaybackException) error;
|
||||||
|
if (exoError.mediaPeriodId != null) {
|
||||||
|
return generateEventTime(new MediaPeriodId(exoError.mediaPeriodId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return generateCurrentPlayerMediaPeriodEventTime();
|
||||||
|
}
|
||||||
|
|
||||||
/** Keeps track of the active media periods and currently playing and reading media period. */
|
/** Keeps track of the active media periods and currently playing and reading media period. */
|
||||||
private static final class MediaPeriodQueueTracker {
|
private static final class MediaPeriodQueueTracker {
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.AudioAttributes;
|
import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.DeviceInfo;
|
||||||
import androidx.media3.common.FlagSet;
|
import androidx.media3.common.FlagSet;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
@ -47,8 +48,10 @@ import androidx.media3.common.Timeline;
|
|||||||
import androidx.media3.common.TrackGroupArray;
|
import androidx.media3.common.TrackGroupArray;
|
||||||
import androidx.media3.common.TrackSelection;
|
import androidx.media3.common.TrackSelection;
|
||||||
import androidx.media3.common.TrackSelectionArray;
|
import androidx.media3.common.TrackSelectionArray;
|
||||||
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
|
import androidx.media3.common.text.Cue;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.decoder.DecoderException;
|
import androidx.media3.decoder.DecoderException;
|
||||||
import androidx.media3.exoplayer.DecoderCounters;
|
import androidx.media3.exoplayer.DecoderCounters;
|
||||||
@ -66,6 +69,7 @@ import java.lang.annotation.Documented;
|
|||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener for analytics events.
|
* A listener for analytics events.
|
||||||
@ -184,6 +188,10 @@ public interface AnalyticsListener {
|
|||||||
EVENT_PLAYLIST_METADATA_CHANGED,
|
EVENT_PLAYLIST_METADATA_CHANGED,
|
||||||
EVENT_SEEK_BACK_INCREMENT_CHANGED,
|
EVENT_SEEK_BACK_INCREMENT_CHANGED,
|
||||||
EVENT_SEEK_FORWARD_INCREMENT_CHANGED,
|
EVENT_SEEK_FORWARD_INCREMENT_CHANGED,
|
||||||
|
EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED,
|
||||||
|
EVENT_TRACK_SELECTION_PARAMETERS_CHANGED,
|
||||||
|
EVENT_DEVICE_INFO_CHANGED,
|
||||||
|
EVENT_DEVICE_VOLUME_CHANGED,
|
||||||
EVENT_LOAD_STARTED,
|
EVENT_LOAD_STARTED,
|
||||||
EVENT_LOAD_COMPLETED,
|
EVENT_LOAD_COMPLETED,
|
||||||
EVENT_LOAD_CANCELED,
|
EVENT_LOAD_CANCELED,
|
||||||
@ -192,6 +200,7 @@ public interface AnalyticsListener {
|
|||||||
EVENT_UPSTREAM_DISCARDED,
|
EVENT_UPSTREAM_DISCARDED,
|
||||||
EVENT_BANDWIDTH_ESTIMATE,
|
EVENT_BANDWIDTH_ESTIMATE,
|
||||||
EVENT_METADATA,
|
EVENT_METADATA,
|
||||||
|
EVENT_CUES,
|
||||||
EVENT_AUDIO_ENABLED,
|
EVENT_AUDIO_ENABLED,
|
||||||
EVENT_AUDIO_DECODER_INITIALIZED,
|
EVENT_AUDIO_DECODER_INITIALIZED,
|
||||||
EVENT_AUDIO_INPUT_FORMAT_CHANGED,
|
EVENT_AUDIO_INPUT_FORMAT_CHANGED,
|
||||||
@ -272,6 +281,8 @@ public interface AnalyticsListener {
|
|||||||
/** {@link Player#getMaxSeekToPreviousPosition()} changed. */
|
/** {@link Player#getMaxSeekToPreviousPosition()} changed. */
|
||||||
int EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED =
|
int EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED =
|
||||||
Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED;
|
Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED;
|
||||||
|
/** {@link Player#getTrackSelectionParameters()} changed. */
|
||||||
|
int EVENT_TRACK_SELECTION_PARAMETERS_CHANGED = Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED;
|
||||||
/** Audio attributes changed. */
|
/** Audio attributes changed. */
|
||||||
int EVENT_AUDIO_ATTRIBUTES_CHANGED = Player.EVENT_AUDIO_ATTRIBUTES_CHANGED;
|
int EVENT_AUDIO_ATTRIBUTES_CHANGED = Player.EVENT_AUDIO_ATTRIBUTES_CHANGED;
|
||||||
/** An audio session id was set. */
|
/** An audio session id was set. */
|
||||||
@ -291,9 +302,12 @@ public interface AnalyticsListener {
|
|||||||
int EVENT_RENDERED_FIRST_FRAME = Player.EVENT_RENDERED_FIRST_FRAME;
|
int EVENT_RENDERED_FIRST_FRAME = Player.EVENT_RENDERED_FIRST_FRAME;
|
||||||
/** Metadata associated with the current playback time was reported. */
|
/** Metadata associated with the current playback time was reported. */
|
||||||
int EVENT_METADATA = Player.EVENT_METADATA;
|
int EVENT_METADATA = Player.EVENT_METADATA;
|
||||||
|
/** {@link Player#getCurrentCues()} changed. */
|
||||||
// TODO: Forward EVENT_CUES, EVENT_DEVICE_INFO_CHANGED and EVENT_DEVICE_VOLUME_CHANGED.
|
int EVENT_CUES = Player.EVENT_CUES;
|
||||||
|
/** {@link Player#getDeviceInfo()} changed. */
|
||||||
|
int EVENT_DEVICE_INFO_CHANGED = Player.EVENT_DEVICE_INFO_CHANGED;
|
||||||
|
/** {@link Player#getDeviceVolume()} changed. */
|
||||||
|
int EVENT_DEVICE_VOLUME_CHANGED = Player.EVENT_DEVICE_VOLUME_CHANGED;
|
||||||
/** A source started loading data. */
|
/** A source started loading data. */
|
||||||
int EVENT_LOAD_STARTED = 1000; // Intentional gap to leave space for new Player events
|
int EVENT_LOAD_STARTED = 1000; // Intentional gap to leave space for new Player events
|
||||||
/** A source started completed loading data. */
|
/** A source started completed loading data. */
|
||||||
@ -683,6 +697,17 @@ public interface AnalyticsListener {
|
|||||||
*/
|
*/
|
||||||
default void onPlayerError(EventTime eventTime, PlaybackException error) {}
|
default void onPlayerError(EventTime eventTime, PlaybackException error) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the {@link PlaybackException} returned by {@link Player#getPlayerError()} changes.
|
||||||
|
*
|
||||||
|
* <p>Implementations of Player may pass an instance of a subclass of {@link PlaybackException} to
|
||||||
|
* this method in order to include more information about the error.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param error The new error, or null if the error is being cleared.
|
||||||
|
*/
|
||||||
|
default void onPlayerErrorChanged(EventTime eventTime, @Nullable PlaybackException error) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the available or selected tracks for the renderers changed.
|
* Called when the available or selected tracks for the renderers changed.
|
||||||
*
|
*
|
||||||
@ -703,6 +728,15 @@ public interface AnalyticsListener {
|
|||||||
*/
|
*/
|
||||||
default void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {}
|
default void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when track selection parameters change.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param trackSelectionParameters The new {@link TrackSelectionParameters}.
|
||||||
|
*/
|
||||||
|
default void onTrackSelectionParametersChanged(
|
||||||
|
EventTime eventTime, TrackSelectionParameters trackSelectionParameters) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the combined {@link MediaMetadata} changes.
|
* Called when the combined {@link MediaMetadata} changes.
|
||||||
*
|
*
|
||||||
@ -812,6 +846,17 @@ public interface AnalyticsListener {
|
|||||||
*/
|
*/
|
||||||
default void onMetadata(EventTime eventTime, Metadata metadata) {}
|
default void onMetadata(EventTime eventTime, Metadata metadata) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when there is a change in the {@link Cue Cues}.
|
||||||
|
*
|
||||||
|
* <p>{@code cues} is in ascending order of priority. If any of the cue boxes overlap when
|
||||||
|
* displayed, the {@link Cue} nearer the end of the list should be shown on top.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param cues The {@link Cue Cues}. May be empty.
|
||||||
|
*/
|
||||||
|
default void onCues(EventTime eventTime, List<Cue> cues) {}
|
||||||
|
|
||||||
/** @deprecated Use {@link #onAudioEnabled} and {@link #onVideoEnabled} instead. */
|
/** @deprecated Use {@link #onAudioEnabled} and {@link #onVideoEnabled} instead. */
|
||||||
@Deprecated
|
@Deprecated
|
||||||
default void onDecoderEnabled(
|
default void onDecoderEnabled(
|
||||||
@ -989,6 +1034,23 @@ public interface AnalyticsListener {
|
|||||||
*/
|
*/
|
||||||
default void onVolumeChanged(EventTime eventTime, float volume) {}
|
default void onVolumeChanged(EventTime eventTime, float volume) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the device information changes
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param deviceInfo The new {@link DeviceInfo}.
|
||||||
|
*/
|
||||||
|
default void onDeviceInfoChanged(EventTime eventTime, DeviceInfo deviceInfo) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the device volume or mute state changes.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param volume The new device volume, with 0 being silence and 1 being unity gain.
|
||||||
|
* @param muted Whether the device is muted.
|
||||||
|
*/
|
||||||
|
default void onDeviceVolumeChanged(EventTime eventTime, int volume, boolean muted) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a video renderer is enabled.
|
* Called when a video renderer is enabled.
|
||||||
*
|
*
|
||||||
|
@ -132,7 +132,7 @@ public interface AudioRendererEventListener {
|
|||||||
/**
|
/**
|
||||||
* Called when {@link AudioSink} has encountered an error.
|
* Called when {@link AudioSink} has encountered an error.
|
||||||
*
|
*
|
||||||
* <p>If the sink writes to a platform {@link AudioTrack}, this will called for all {@link
|
* <p>If the sink writes to a platform {@link AudioTrack}, this will be called for all {@link
|
||||||
* AudioTrack} errors.
|
* AudioTrack} errors.
|
||||||
*
|
*
|
||||||
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||||
|
@ -121,6 +121,7 @@ import androidx.test.core.app.ApplicationProvider;
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -191,6 +192,18 @@ public final class AnalyticsCollectorTest {
|
|||||||
private EventWindowAndPeriodId window0Period1Seq0;
|
private EventWindowAndPeriodId window0Period1Seq0;
|
||||||
private EventWindowAndPeriodId window1Period0Seq1;
|
private EventWindowAndPeriodId window1Period0Seq1;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void analyticsCollector_overridesAllPlayerListenerMethods() throws Exception {
|
||||||
|
// Verify that AnalyticsCollector forwards all Player.Listener methods to AnalyticsListener.
|
||||||
|
for (Method method : Player.Listener.class.getDeclaredMethods()) {
|
||||||
|
assertThat(
|
||||||
|
AnalyticsCollector.class
|
||||||
|
.getMethod(method.getName(), method.getParameterTypes())
|
||||||
|
.getDeclaringClass())
|
||||||
|
.isEqualTo(AnalyticsCollector.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyTimeline() throws Exception {
|
public void emptyTimeline() throws Exception {
|
||||||
FakeMediaSource mediaSource =
|
FakeMediaSource mediaSource =
|
||||||
@ -434,7 +447,7 @@ public final class AnalyticsCollectorTest {
|
|||||||
// Wait until second period has fully loaded to assert loading events without flakiness.
|
// Wait until second period has fully loaded to assert loading events without flakiness.
|
||||||
.waitForIsLoading(true)
|
.waitForIsLoading(true)
|
||||||
.waitForIsLoading(false)
|
.waitForIsLoading(false)
|
||||||
.seek(/* windowIndex= */ 1, /* positionMs= */ 0)
|
.seek(/* mediaItemIndex= */ 1, /* positionMs= */ 0)
|
||||||
.play()
|
.play()
|
||||||
.build();
|
.build();
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
|
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
|
||||||
@ -527,7 +540,7 @@ public final class AnalyticsCollectorTest {
|
|||||||
new ActionSchedule.Builder(TAG)
|
new ActionSchedule.Builder(TAG)
|
||||||
.pause()
|
.pause()
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
.waitForPlaybackState(Player.STATE_READY)
|
||||||
.playUntilPosition(/* windowIndex= */ 0, periodDurationMs)
|
.playUntilPosition(/* mediaItemIndex= */ 0, periodDurationMs)
|
||||||
.seekAndWait(/* positionMs= */ 0)
|
.seekAndWait(/* positionMs= */ 0)
|
||||||
.play()
|
.play()
|
||||||
.build();
|
.build();
|
||||||
@ -821,7 +834,7 @@ public final class AnalyticsCollectorTest {
|
|||||||
.pause()
|
.pause()
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
.waitForPlaybackState(Player.STATE_READY)
|
||||||
// Ensure second period is already being read from.
|
// Ensure second period is already being read from.
|
||||||
.playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ periodDurationMs)
|
.playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ periodDurationMs)
|
||||||
.executeRunnable(
|
.executeRunnable(
|
||||||
() ->
|
() ->
|
||||||
concatenatedMediaSource.moveMediaSource(
|
concatenatedMediaSource.moveMediaSource(
|
||||||
@ -1086,9 +1099,9 @@ public final class AnalyticsCollectorTest {
|
|||||||
.waitForPlaybackState(Player.STATE_READY)
|
.waitForPlaybackState(Player.STATE_READY)
|
||||||
// Wait in each content part to ensure previously triggered events get a chance to be
|
// Wait in each content part to ensure previously triggered events get a chance to be
|
||||||
// delivered. This prevents flakiness caused by playback progressing too fast.
|
// delivered. This prevents flakiness caused by playback progressing too fast.
|
||||||
.playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ 3_000)
|
.playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 3_000)
|
||||||
.waitForPendingPlayerCommands()
|
.waitForPendingPlayerCommands()
|
||||||
.playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ 8_000)
|
.playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 8_000)
|
||||||
.waitForPendingPlayerCommands()
|
.waitForPendingPlayerCommands()
|
||||||
.play()
|
.play()
|
||||||
.waitForPlaybackState(Player.STATE_ENDED)
|
.waitForPlaybackState(Player.STATE_ENDED)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user