未验证 提交 9830c773 编写于 作者: O Oliver Woodman 提交者: GitHub

Merge pull request #7455 from google/dev-v2-r2.11.5-7193

Merge fix for #7193 into dev-v2-r2.11.5
......@@ -29,7 +29,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* Default {@link PlaybackSessionManager} which instantiates a new session for each window in the
......@@ -48,8 +47,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
private @MonotonicNonNull Listener listener;
private Timeline currentTimeline;
@Nullable private MediaPeriodId currentMediaPeriodId;
@Nullable private String activeSessionId;
@Nullable private String currentSessionId;
/** Creates session manager. */
public DefaultPlaybackSessionManager() {
......@@ -83,22 +81,34 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
@Override
public synchronized void updateSessions(EventTime eventTime) {
boolean isObviouslyFinished =
eventTime.mediaPeriodId != null
&& currentMediaPeriodId != null
&& eventTime.mediaPeriodId.windowSequenceNumber
< currentMediaPeriodId.windowSequenceNumber;
if (!isObviouslyFinished) {
SessionDescriptor descriptor =
getOrAddSession(eventTime.windowIndex, eventTime.mediaPeriodId);
if (!descriptor.isCreated) {
descriptor.isCreated = true;
Assertions.checkNotNull(listener).onSessionCreated(eventTime, descriptor.sessionId);
if (activeSessionId == null) {
updateActiveSession(eventTime, descriptor);
}
Assertions.checkNotNull(listener);
@Nullable SessionDescriptor currentSession = sessions.get(currentSessionId);
if (eventTime.mediaPeriodId != null && currentSession != null) {
// If we receive an event associated with a media period, then it needs to be either part of
// the current window if it's the first created media period, or a window that will be played
// in the future. Otherwise, we know that it belongs to a session that was already finished
// and we can ignore the event.
boolean isAlreadyFinished =
currentSession.windowSequenceNumber == C.INDEX_UNSET
? currentSession.windowIndex != eventTime.windowIndex
: eventTime.mediaPeriodId.windowSequenceNumber < currentSession.windowSequenceNumber;
if (isAlreadyFinished) {
return;
}
}
SessionDescriptor eventSession =
getOrAddSession(eventTime.windowIndex, eventTime.mediaPeriodId);
if (currentSessionId == null) {
currentSessionId = eventSession.sessionId;
}
if (!eventSession.isCreated) {
eventSession.isCreated = true;
listener.onSessionCreated(eventTime, eventSession.sessionId);
}
if (eventSession.sessionId.equals(currentSessionId) && !eventSession.isActive) {
eventSession.isActive = true;
listener.onSessionActive(eventTime, eventSession.sessionId);
}
}
@Override
......@@ -112,8 +122,8 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
if (!session.tryResolvingToNewTimeline(previousTimeline, currentTimeline)) {
iterator.remove();
if (session.isCreated) {
if (session.sessionId.equals(activeSessionId)) {
activeSessionId = null;
if (session.sessionId.equals(currentSessionId)) {
currentSessionId = null;
}
listener.onSessionFinished(
eventTime, session.sessionId, /* automaticTransitionToNextPlayback= */ false);
......@@ -136,36 +146,55 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
if (session.isFinishedAtEventTime(eventTime)) {
iterator.remove();
if (session.isCreated) {
boolean isRemovingActiveSession = session.sessionId.equals(activeSessionId);
boolean isAutomaticTransition = hasAutomaticTransition && isRemovingActiveSession;
if (isRemovingActiveSession) {
activeSessionId = null;
boolean isRemovingCurrentSession = session.sessionId.equals(currentSessionId);
boolean isAutomaticTransition =
hasAutomaticTransition && isRemovingCurrentSession && session.isActive;
if (isRemovingCurrentSession) {
currentSessionId = null;
}
listener.onSessionFinished(eventTime, session.sessionId, isAutomaticTransition);
}
}
}
SessionDescriptor activeSessionDescriptor =
@Nullable SessionDescriptor previousSessionDescriptor = sessions.get(currentSessionId);
SessionDescriptor currentSessionDescriptor =
getOrAddSession(eventTime.windowIndex, eventTime.mediaPeriodId);
currentSessionId = currentSessionDescriptor.sessionId;
if (eventTime.mediaPeriodId != null
&& eventTime.mediaPeriodId.isAd()
&& (currentMediaPeriodId == null
|| currentMediaPeriodId.windowSequenceNumber
&& (previousSessionDescriptor == null
|| previousSessionDescriptor.windowSequenceNumber
!= eventTime.mediaPeriodId.windowSequenceNumber
|| currentMediaPeriodId.adGroupIndex != eventTime.mediaPeriodId.adGroupIndex
|| currentMediaPeriodId.adIndexInAdGroup != eventTime.mediaPeriodId.adIndexInAdGroup)) {
|| previousSessionDescriptor.adMediaPeriodId == null
|| previousSessionDescriptor.adMediaPeriodId.adGroupIndex
!= eventTime.mediaPeriodId.adGroupIndex
|| previousSessionDescriptor.adMediaPeriodId.adIndexInAdGroup
!= eventTime.mediaPeriodId.adIndexInAdGroup)) {
// New ad playback started. Find corresponding content session and notify ad playback started.
MediaPeriodId contentMediaPeriodId =
new MediaPeriodId(
eventTime.mediaPeriodId.periodUid, eventTime.mediaPeriodId.windowSequenceNumber);
SessionDescriptor contentSession =
getOrAddSession(eventTime.windowIndex, contentMediaPeriodId);
if (contentSession.isCreated && activeSessionDescriptor.isCreated) {
if (contentSession.isCreated && currentSessionDescriptor.isCreated) {
listener.onAdPlaybackStarted(
eventTime, contentSession.sessionId, activeSessionDescriptor.sessionId);
eventTime, contentSession.sessionId, currentSessionDescriptor.sessionId);
}
}
}
@Override
public void finishAllSessions(EventTime eventTime) {
currentSessionId = null;
Iterator<SessionDescriptor> iterator = sessions.values().iterator();
while (iterator.hasNext()) {
SessionDescriptor session = iterator.next();
iterator.remove();
if (session.isCreated && listener != null) {
listener.onSessionFinished(
eventTime, session.sessionId, /* automaticTransitionToNextPlayback= */ false);
}
}
updateActiveSession(eventTime, activeSessionDescriptor);
}
private SessionDescriptor getOrAddSession(
......@@ -199,18 +228,6 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
return bestMatch;
}
@RequiresNonNull("listener")
private void updateActiveSession(EventTime eventTime, SessionDescriptor sessionDescriptor) {
currentMediaPeriodId = eventTime.mediaPeriodId;
if (sessionDescriptor.isCreated) {
activeSessionId = sessionDescriptor.sessionId;
if (!sessionDescriptor.isActive) {
sessionDescriptor.isActive = true;
listener.onSessionActive(eventTime, sessionDescriptor.sessionId);
}
}
}
private static String generateSessionId() {
byte[] randomBytes = new byte[SESSION_ID_LENGTH];
RANDOM.nextBytes(randomBytes);
......@@ -284,8 +301,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
int eventWindowIndex, @Nullable MediaPeriodId eventMediaPeriodId) {
if (windowSequenceNumber == C.INDEX_UNSET
&& eventWindowIndex == windowIndex
&& eventMediaPeriodId != null
&& !eventMediaPeriodId.isAd()) {
&& eventMediaPeriodId != null) {
// Set window sequence number for this session as soon as we have one.
windowSequenceNumber = eventMediaPeriodId.windowSequenceNumber;
}
......
......@@ -117,4 +117,12 @@ public interface PlaybackSessionManager {
* @param reason The {@link DiscontinuityReason}.
*/
void handlePositionDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason);
/**
* Finishes all existing sessions and calls their respective {@link
* Listener#onSessionFinished(EventTime, String, boolean)} callback.
*
* @param eventTime The event time at which sessions are finished.
*/
void finishAllSessions(EventTime eventTime);
}
......@@ -148,7 +148,6 @@ public final class PlaybackStatsListener
// TODO: Add AnalyticsListener.onAttachedToPlayer and onDetachedFromPlayer to auto-release with
// an actual EventTime. Should also simplify other cases where the listener needs to be released
// separately from the player.
HashMap<String, PlaybackStatsTracker> trackerCopy = new HashMap<>(playbackStatsTrackers);
EventTime dummyEventTime =
new EventTime(
SystemClock.elapsedRealtime(),
......@@ -158,9 +157,7 @@ public final class PlaybackStatsListener
/* eventPlaybackPositionMs= */ 0,
/* currentPlaybackPositionMs= */ 0,
/* totalBufferedDurationMs= */ 0);
for (String session : trackerCopy.keySet()) {
onSessionFinished(dummyEventTime, session, /* automaticTransition= */ false);
}
sessionManager.finishAllSessions(dummyEventTime);
}
// PlaybackSessionManager.Listener implementation.
......@@ -243,7 +240,7 @@ public final class PlaybackStatsListener
EventTime eventTime, boolean playWhenReady, @Player.State int playbackState) {
this.playWhenReady = playWhenReady;
this.playbackState = playbackState;
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
boolean belongsToPlayback = sessionManager.belongsToSession(eventTime, session);
playbackStatsTrackers
......@@ -256,7 +253,7 @@ public final class PlaybackStatsListener
public void onPlaybackSuppressionReasonChanged(
EventTime eventTime, int playbackSuppressionReason) {
isSuppressed = playbackSuppressionReason != Player.PLAYBACK_SUPPRESSION_REASON_NONE;
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
boolean belongsToPlayback = sessionManager.belongsToSession(eventTime, session);
playbackStatsTrackers
......@@ -268,7 +265,7 @@ public final class PlaybackStatsListener
@Override
public void onTimelineChanged(EventTime eventTime, int reason) {
sessionManager.handleTimelineUpdate(eventTime);
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onPositionDiscontinuity(eventTime);
......@@ -279,7 +276,7 @@ public final class PlaybackStatsListener
@Override
public void onPositionDiscontinuity(EventTime eventTime, int reason) {
sessionManager.handlePositionDiscontinuity(eventTime, reason);
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onPositionDiscontinuity(eventTime);
......@@ -289,7 +286,7 @@ public final class PlaybackStatsListener
@Override
public void onSeekStarted(EventTime eventTime) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onSeekStarted(eventTime);
......@@ -299,7 +296,7 @@ public final class PlaybackStatsListener
@Override
public void onSeekProcessed(EventTime eventTime) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onSeekProcessed(eventTime);
......@@ -309,7 +306,7 @@ public final class PlaybackStatsListener
@Override
public void onPlayerError(EventTime eventTime, ExoPlaybackException error) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onFatalError(eventTime, error);
......@@ -321,7 +318,7 @@ public final class PlaybackStatsListener
public void onPlaybackParametersChanged(
EventTime eventTime, PlaybackParameters playbackParameters) {
playbackSpeed = playbackParameters.speed;
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (PlaybackStatsTracker tracker : playbackStatsTrackers.values()) {
tracker.onPlaybackSpeedChanged(eventTime, playbackSpeed);
}
......@@ -330,7 +327,7 @@ public final class PlaybackStatsListener
@Override
public void onTracksChanged(
EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onTracksChanged(eventTime, trackSelections);
......@@ -341,7 +338,7 @@ public final class PlaybackStatsListener
@Override
public void onLoadStarted(
EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onLoadStarted(eventTime);
......@@ -351,7 +348,7 @@ public final class PlaybackStatsListener
@Override
public void onDownstreamFormatChanged(EventTime eventTime, MediaLoadData mediaLoadData) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onDownstreamFormatChanged(eventTime, mediaLoadData);
......@@ -366,7 +363,7 @@ public final class PlaybackStatsListener
int height,
int unappliedRotationDegrees,
float pixelWidthHeightRatio) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onVideoSizeChanged(eventTime, width, height);
......@@ -377,7 +374,7 @@ public final class PlaybackStatsListener
@Override
public void onBandwidthEstimate(
EventTime eventTime, int totalLoadTimeMs, long totalBytesLoaded, long bitrateEstimate) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onBandwidthData(totalLoadTimeMs, totalBytesLoaded);
......@@ -388,7 +385,7 @@ public final class PlaybackStatsListener
@Override
public void onAudioUnderrun(
EventTime eventTime, int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onAudioUnderrun();
......@@ -398,7 +395,7 @@ public final class PlaybackStatsListener
@Override
public void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onDroppedVideoFrames(droppedFrames);
......@@ -413,7 +410,7 @@ public final class PlaybackStatsListener
MediaLoadData mediaLoadData,
IOException error,
boolean wasCanceled) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onNonFatalError(eventTime, error);
......@@ -423,7 +420,7 @@ public final class PlaybackStatsListener
@Override
public void onDrmSessionManagerError(EventTime eventTime, Exception error) {
sessionManager.updateSessions(eventTime);
maybeAddSession(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onNonFatalError(eventTime, error);
......@@ -431,6 +428,13 @@ public final class PlaybackStatsListener
}
}
private void maybeAddSession(EventTime eventTime) {
boolean isCompletelyIdle = eventTime.timeline.isEmpty() && playbackState == Player.STATE_IDLE;
if (!isCompletelyIdle) {
sessionManager.updateSessions(eventTime);
}
}
/** Tracker for playback stats of a single playback. */
private static final class PlaybackStatsTracker {
......
......@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
......@@ -501,6 +502,7 @@ public final class DefaultPlaybackSessionManagerTest {
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
sessionManager.handleTimelineUpdate(newTimelineEventTime);
sessionManager.updateSessions(newTimelineEventTime);
ArgumentCaptor<String> sessionId1 = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> sessionId2 = ArgumentCaptor.forClass(String.class);
......@@ -657,6 +659,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.handlePositionDiscontinuity(
eventTime2, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
sessionManager.updateSessions(eventTime2);
verify(mockListener).onSessionCreated(eq(eventTime1), anyString());
verify(mockListener).onSessionActive(eq(eventTime1), anyString());
......@@ -688,6 +691,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.handlePositionDiscontinuity(
eventTime2, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
sessionManager.updateSessions(eventTime2);
verify(mockListener).onSessionCreated(eventTime1, sessionId1);
verify(mockListener).onSessionActive(eventTime1, sessionId1);
......@@ -722,6 +726,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.getSessionForMediaPeriodId(timeline, eventTime2.mediaPeriodId);
sessionManager.handlePositionDiscontinuity(eventTime2, Player.DISCONTINUITY_REASON_SEEK);
sessionManager.updateSessions(eventTime2);
verify(mockListener).onSessionCreated(eventTime1, sessionId1);
verify(mockListener).onSessionActive(eventTime1, sessionId1);
......@@ -748,6 +753,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.updateSessions(eventTime2);
sessionManager.handlePositionDiscontinuity(eventTime2, Player.DISCONTINUITY_REASON_SEEK);
sessionManager.updateSessions(eventTime2);
verify(mockListener, never()).onSessionFinished(any(), anyString(), anyBoolean());
}
......@@ -790,6 +796,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.getSessionForMediaPeriodId(timeline, eventTime2.mediaPeriodId);
sessionManager.handlePositionDiscontinuity(eventTime3, Player.DISCONTINUITY_REASON_SEEK);
sessionManager.updateSessions(eventTime3);
verify(mockListener).onSessionCreated(eventTime1, sessionId1);
verify(mockListener).onSessionActive(eventTime1, sessionId1);
......@@ -851,6 +858,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.handlePositionDiscontinuity(
contentEventTime, Player.DISCONTINUITY_REASON_AD_INSERTION);
sessionManager.updateSessions(contentEventTime);
verify(mockListener).onSessionCreated(adEventTime1, adSessionId1);
verify(mockListener).onSessionActive(adEventTime1, adSessionId1);
......@@ -858,6 +866,8 @@ public final class DefaultPlaybackSessionManagerTest {
verify(mockListener)
.onSessionFinished(
contentEventTime, adSessionId1, /* automaticTransitionToNextPlayback= */ true);
verify(mockListener).onSessionCreated(eq(contentEventTime), anyString());
verify(mockListener).onSessionActive(eq(contentEventTime), anyString());
verifyNoMoreInteractions(mockListener);
}
......@@ -908,6 +918,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.handlePositionDiscontinuity(
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION);
sessionManager.updateSessions(adEventTime1);
verify(mockListener, never()).onSessionFinished(any(), anyString(), anyBoolean());
}
......@@ -964,7 +975,9 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.handlePositionDiscontinuity(
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION);
sessionManager.updateSessions(adEventTime1);
sessionManager.handlePositionDiscontinuity(adEventTime2, Player.DISCONTINUITY_REASON_SEEK);
sessionManager.updateSessions(adEventTime2);
verify(mockListener).onSessionCreated(eq(contentEventTime), anyString());
verify(mockListener).onSessionActive(eq(contentEventTime), anyString());
......@@ -1034,8 +1047,10 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.updateSessions(adEventTime1);
sessionManager.handlePositionDiscontinuity(
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION);
sessionManager.updateSessions(adEventTime1);
sessionManager.handlePositionDiscontinuity(
contentEventTime2, Player.DISCONTINUITY_REASON_AD_INSERTION);
sessionManager.updateSessions(contentEventTime2);
String adSessionId2 =
sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime2.mediaPeriodId);
......@@ -1044,6 +1059,31 @@ public final class DefaultPlaybackSessionManagerTest {
verify(mockListener, never()).onSessionActive(any(), eq(adSessionId2));
}
@Test
public void finishAllSessions_callsOnSessionFinishedForAllCreatedSessions() {
Timeline timeline = new FakeTimeline(/* windowCount= */ 4);
EventTime eventTimeWindow0 =
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
EventTime eventTimeWindow2 =
createEventTime(timeline, /* windowIndex= */ 2, /* mediaPeriodId= */ null);
// Actually create sessions for window 0 and 2.
sessionManager.updateSessions(eventTimeWindow0);
sessionManager.updateSessions(eventTimeWindow2);
// Query information about session for window 1, but don't create it.
sessionManager.getSessionForMediaPeriodId(
timeline,
new MediaPeriodId(
timeline.getPeriod(/* periodIndex= */ 1, new Timeline.Period(), /* setIds= */ true).uid,
/* windowSequenceNumber= */ 123));
verify(mockListener, times(2)).onSessionCreated(any(), anyString());
EventTime finishEventTime =
createEventTime(Timeline.EMPTY, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
sessionManager.finishAllSessions(finishEventTime);
verify(mockListener, times(2)).onSessionFinished(eq(finishEventTime), anyString(), eq(false));
}
private static EventTime createEventTime(
Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
return new EventTime(
......
......@@ -16,11 +16,20 @@
package com.google.android.exoplayer2.analytics;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.SystemClock;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -28,7 +37,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public final class PlaybackStatsListenerTest {
private static final AnalyticsListener.EventTime TEST_EVENT_TIME =
private static final AnalyticsListener.EventTime EMPTY_TIMELINE_EVENT_TIME =
new AnalyticsListener.EventTime(
/* realtimeMs= */ 500,
Timeline.EMPTY,
......@@ -37,6 +46,41 @@ public final class PlaybackStatsListenerTest {
/* eventPlaybackPositionMs= */ 0,
/* currentPlaybackPositionMs= */ 0,
/* totalBufferedDurationMs= */ 0);
private static final Timeline TEST_TIMELINE = new FakeTimeline(/* windowCount= */ 1);
private static final AnalyticsListener.EventTime TEST_EVENT_TIME =
new AnalyticsListener.EventTime(
/* realtimeMs= */ 500,
TEST_TIMELINE,
/* windowIndex= */ 0,
new MediaSource.MediaPeriodId(
TEST_TIMELINE.getPeriod(
/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true)
.uid,
/* windowSequenceNumber= */ 42),
/* eventPlaybackPositionMs= */ 123,
/* currentPlaybackPositionMs= */ 123,
/* totalBufferedDurationMs= */ 456);
@Test
public void stateChangeEvent_toNonIdle_createsInitialPlaybackStats() {
PlaybackStatsListener playbackStatsListener =
new PlaybackStatsListener(/* keepHistory= */ true, /* callback= */ null);
playbackStatsListener.onPlayerStateChanged(
EMPTY_TIMELINE_EVENT_TIME, /* playWhenReady= */ false, Player.STATE_BUFFERING);
assertThat(playbackStatsListener.getPlaybackStats()).isNotNull();
}
@Test
public void timelineChangeEvent_toNonEmpty_createsInitialPlaybackStats() {
PlaybackStatsListener playbackStatsListener =
new PlaybackStatsListener(/* keepHistory= */ true, /* callback= */ null);
playbackStatsListener.onTimelineChanged(TEST_EVENT_TIME, Player.TIMELINE_CHANGE_REASON_DYNAMIC);
assertThat(playbackStatsListener.getPlaybackStats()).isNotNull();
}
@Test
public void playback_withKeepHistory_updatesStats() {
......@@ -71,4 +115,72 @@ public final class PlaybackStatsListenerTest {
assertThat(playbackStats).isNotNull();
assertThat(playbackStats.endedCount).isEqualTo(1);
}
@Test
public void finishedSession_callsCallback() {
PlaybackStatsListener.Callback callback = mock(PlaybackStatsListener.Callback.class);
PlaybackStatsListener playbackStatsListener =
new PlaybackStatsListener(/* keepHistory= */ true, callback);
// Create session with an event and finish it by simulating removal from playlist.
playbackStatsListener.onPlayerStateChanged(
TEST_EVENT_TIME, /* playWhenReady= */ false, Player.STATE_BUFFERING);
verify(callback, never()).onPlaybackStatsReady(any(), any());
playbackStatsListener.onTimelineChanged(
EMPTY_TIMELINE_EVENT_TIME, Player.TIMELINE_CHANGE_REASON_DYNAMIC);
verify(callback).onPlaybackStatsReady(eq(TEST_EVENT_TIME), any());
}
@Test
public void finishAllSessions_callsAllPendingCallbacks() {
AnalyticsListener.EventTime eventTimeWindow0 =
new AnalyticsListener.EventTime(
/* realtimeMs= */ 0,
Timeline.EMPTY,
/* windowIndex= */ 0,
/* mediaPeriodId= */ null,
/* eventPlaybackPositionMs= */ 0,
/* currentPlaybackPositionMs= */ 0,
/* totalBufferedDurationMs= */ 0);
AnalyticsListener.EventTime eventTimeWindow1 =
new AnalyticsListener.EventTime(
/* realtimeMs= */ 0,
Timeline.EMPTY,
/* windowIndex= */ 1,
/* mediaPeriodId= */ null,
/* eventPlaybackPositionMs= */ 0,
/* currentPlaybackPositionMs= */ 0,
/* totalBufferedDurationMs= */ 0);
PlaybackStatsListener.Callback callback = mock(PlaybackStatsListener.Callback.class);
PlaybackStatsListener playbackStatsListener =
new PlaybackStatsListener(/* keepHistory= */ true, callback);
playbackStatsListener.onPlayerStateChanged(
eventTimeWindow0, /* playWhenReady= */ false, Player.STATE_BUFFERING);
playbackStatsListener.onPlayerStateChanged(
eventTimeWindow1, /* playWhenReady= */ false, Player.STATE_BUFFERING);
playbackStatsListener.finishAllSessions();
verify(callback, times(2)).onPlaybackStatsReady(any(), any());
verify(callback).onPlaybackStatsReady(eq(eventTimeWindow0), any());
verify(callback).onPlaybackStatsReady(eq(eventTimeWindow1), any());
}
@Test
public void finishAllSessions_doesNotCallCallbackAgainWhenSessionWouldBeAutomaticallyFinished() {
PlaybackStatsListener.Callback callback = mock(PlaybackStatsListener.Callback.class);
PlaybackStatsListener playbackStatsListener =
new PlaybackStatsListener(/* keepHistory= */ true, callback);
playbackStatsListener.onPlayerStateChanged(
TEST_EVENT_TIME, /* playWhenReady= */ false, Player.STATE_BUFFERING);
SystemClock.setCurrentTimeMillis(TEST_EVENT_TIME.realtimeMs + 100);
playbackStatsListener.finishAllSessions();
// Simulate removing the playback item to ensure the session would finish if it hadn't already.
playbackStatsListener.onTimelineChanged(
EMPTY_TIMELINE_EVENT_TIME, Player.TIMELINE_CHANGE_REASON_DYNAMIC);
verify(callback).onPlaybackStatsReady(any(), any());
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册