mirror of
https://github.com/androidx/media.git
synced 2025-05-17 04:29:55 +08:00
Move playback session manager to core library.
This allows to use the session management capabilities for other analytics purposes. PiperOrigin-RevId: 245710588
This commit is contained in:
parent
880f403181
commit
1fb128df36
@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.analytics;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Base64;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime;
|
||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
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
|
||||
* timeline and also for each ad within the windows.
|
||||
*
|
||||
* <p>Sessions are identified by Base64-encoded, URL-safe, random strings.
|
||||
*/
|
||||
public final class DefaultPlaybackSessionManager implements PlaybackSessionManager {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int SESSION_ID_LENGTH = 12;
|
||||
|
||||
private final Timeline.Window window;
|
||||
private final Timeline.Period period;
|
||||
private final HashMap<String, SessionDescriptor> sessions;
|
||||
|
||||
@MonotonicNonNull private Listener listener;
|
||||
private Timeline currentTimeline;
|
||||
@Nullable private MediaPeriodId currentMediaPeriodId;
|
||||
@Nullable private String activeSessionId;
|
||||
|
||||
/** Creates session manager. */
|
||||
public DefaultPlaybackSessionManager() {
|
||||
window = new Timeline.Window();
|
||||
period = new Timeline.Period();
|
||||
sessions = new HashMap<>();
|
||||
currentTimeline = Timeline.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getSessionForMediaPeriodId(
|
||||
Timeline timeline, MediaPeriodId mediaPeriodId) {
|
||||
int windowIndex = timeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex;
|
||||
return getOrAddSession(windowIndex, mediaPeriodId).sessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean belongsToSession(EventTime eventTime, String sessionId) {
|
||||
SessionDescriptor sessionDescriptor = sessions.get(sessionId);
|
||||
if (sessionDescriptor == null) {
|
||||
return false;
|
||||
}
|
||||
sessionDescriptor.maybeSetWindowSequenceNumber(eventTime.windowIndex, eventTime.mediaPeriodId);
|
||||
return sessionDescriptor.belongsToSession(eventTime.windowIndex, eventTime.mediaPeriodId);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void handleTimelineUpdate(EventTime eventTime) {
|
||||
Assertions.checkNotNull(listener);
|
||||
Timeline previousTimeline = currentTimeline;
|
||||
currentTimeline = eventTime.timeline;
|
||||
Iterator<SessionDescriptor> iterator = sessions.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SessionDescriptor session = iterator.next();
|
||||
if (!session.tryResolvingToNewTimeline(previousTimeline, currentTimeline)) {
|
||||
iterator.remove();
|
||||
if (session.isCreated) {
|
||||
if (session.sessionId.equals(activeSessionId)) {
|
||||
activeSessionId = null;
|
||||
}
|
||||
listener.onSessionFinished(
|
||||
eventTime, session.sessionId, /* automaticTransitionToNextPlayback= */ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
handlePositionDiscontinuity(eventTime, Player.DISCONTINUITY_REASON_INTERNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void handlePositionDiscontinuity(
|
||||
EventTime eventTime, @DiscontinuityReason int reason) {
|
||||
Assertions.checkNotNull(listener);
|
||||
boolean hasAutomaticTransition =
|
||||
reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION
|
||||
|| reason == Player.DISCONTINUITY_REASON_AD_INSERTION;
|
||||
Iterator<SessionDescriptor> iterator = sessions.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SessionDescriptor session = iterator.next();
|
||||
if (session.isFinishedAtEventTime(eventTime)) {
|
||||
iterator.remove();
|
||||
if (session.isCreated) {
|
||||
boolean isRemovingActiveSession = session.sessionId.equals(activeSessionId);
|
||||
boolean isAutomaticTransition = hasAutomaticTransition && isRemovingActiveSession;
|
||||
if (isRemovingActiveSession) {
|
||||
activeSessionId = null;
|
||||
}
|
||||
listener.onSessionFinished(eventTime, session.sessionId, isAutomaticTransition);
|
||||
}
|
||||
}
|
||||
}
|
||||
SessionDescriptor activeSessionDescriptor =
|
||||
getOrAddSession(eventTime.windowIndex, eventTime.mediaPeriodId);
|
||||
if (eventTime.mediaPeriodId != null
|
||||
&& eventTime.mediaPeriodId.isAd()
|
||||
&& (currentMediaPeriodId == null
|
||||
|| currentMediaPeriodId.windowSequenceNumber
|
||||
!= eventTime.mediaPeriodId.windowSequenceNumber
|
||||
|| currentMediaPeriodId.adGroupIndex != eventTime.mediaPeriodId.adGroupIndex
|
||||
|| currentMediaPeriodId.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) {
|
||||
listener.onAdPlaybackStarted(
|
||||
eventTime, contentSession.sessionId, activeSessionDescriptor.sessionId);
|
||||
}
|
||||
}
|
||||
updateActiveSession(eventTime, activeSessionDescriptor);
|
||||
}
|
||||
|
||||
private SessionDescriptor getOrAddSession(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
// There should only be one matching session if mediaPeriodId is non-null. If mediaPeriodId is
|
||||
// null, there may be multiple matching sessions with different window sequence numbers or
|
||||
// adMediaPeriodIds. The best match is the one with the smaller window sequence number, and for
|
||||
// windows with ads, the content session is preferred over ad sessions.
|
||||
SessionDescriptor bestMatch = null;
|
||||
long bestMatchWindowSequenceNumber = Long.MAX_VALUE;
|
||||
for (SessionDescriptor sessionDescriptor : sessions.values()) {
|
||||
sessionDescriptor.maybeSetWindowSequenceNumber(windowIndex, mediaPeriodId);
|
||||
if (sessionDescriptor.belongsToSession(windowIndex, mediaPeriodId)) {
|
||||
long windowSequenceNumber = sessionDescriptor.windowSequenceNumber;
|
||||
if (windowSequenceNumber == C.INDEX_UNSET
|
||||
|| windowSequenceNumber < bestMatchWindowSequenceNumber) {
|
||||
bestMatch = sessionDescriptor;
|
||||
bestMatchWindowSequenceNumber = windowSequenceNumber;
|
||||
} else if (windowSequenceNumber == bestMatchWindowSequenceNumber
|
||||
&& Util.castNonNull(bestMatch).adMediaPeriodId != null
|
||||
&& sessionDescriptor.adMediaPeriodId != null) {
|
||||
bestMatch = sessionDescriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestMatch == null) {
|
||||
String sessionId = generateSessionId();
|
||||
bestMatch = new SessionDescriptor(sessionId, windowIndex, mediaPeriodId);
|
||||
sessions.put(sessionId, bestMatch);
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
@RequiresNonNull("listener")
|
||||
private void updateActiveSession(EventTime eventTime, SessionDescriptor sessionDescriptor) {
|
||||
currentMediaPeriodId = eventTime.mediaPeriodId;
|
||||
if (sessionDescriptor.isCreated && !sessionDescriptor.isActive) {
|
||||
sessionDescriptor.isActive = true;
|
||||
activeSessionId = sessionDescriptor.sessionId;
|
||||
listener.onSessionActive(eventTime, sessionDescriptor.sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateSessionId() {
|
||||
byte[] randomBytes = new byte[SESSION_ID_LENGTH];
|
||||
RANDOM.nextBytes(randomBytes);
|
||||
return Base64.encodeToString(randomBytes, Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptor for a session.
|
||||
*
|
||||
* <p>The session may be described in one of three ways:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A window index with unset window sequence number and a null ad media period id
|
||||
* <li>A content window with index and sequence number, but a null ad media period id.
|
||||
* <li>An ad with all values set.
|
||||
* </ul>
|
||||
*/
|
||||
private final class SessionDescriptor {
|
||||
|
||||
private final String sessionId;
|
||||
|
||||
private int windowIndex;
|
||||
private long windowSequenceNumber;
|
||||
private @MonotonicNonNull MediaPeriodId adMediaPeriodId;
|
||||
|
||||
private boolean isCreated;
|
||||
private boolean isActive;
|
||||
|
||||
public SessionDescriptor(
|
||||
String sessionId, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
this.sessionId = sessionId;
|
||||
this.windowIndex = windowIndex;
|
||||
this.windowSequenceNumber =
|
||||
mediaPeriodId == null ? C.INDEX_UNSET : mediaPeriodId.windowSequenceNumber;
|
||||
if (mediaPeriodId != null && mediaPeriodId.isAd()) {
|
||||
this.adMediaPeriodId = mediaPeriodId;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryResolvingToNewTimeline(Timeline oldTimeline, Timeline newTimeline) {
|
||||
windowIndex = resolveWindowIndexToNewTimeline(oldTimeline, newTimeline, windowIndex);
|
||||
if (windowIndex == C.INDEX_UNSET) {
|
||||
return false;
|
||||
}
|
||||
if (adMediaPeriodId != null) {
|
||||
int newPeriodIndex = newTimeline.getIndexOfPeriod(adMediaPeriodId.periodUid);
|
||||
if (newPeriodIndex == C.INDEX_UNSET) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean belongsToSession(
|
||||
int eventWindowIndex, @Nullable MediaPeriodId eventMediaPeriodId) {
|
||||
if (eventMediaPeriodId == null) {
|
||||
// Events without concrete media period id are for all sessions of the same window.
|
||||
return eventWindowIndex == windowIndex;
|
||||
}
|
||||
if (adMediaPeriodId == null) {
|
||||
// If this is a content session, only events for content with the same window sequence
|
||||
// number belong to this session.
|
||||
return !eventMediaPeriodId.isAd()
|
||||
&& eventMediaPeriodId.windowSequenceNumber == windowSequenceNumber;
|
||||
}
|
||||
// If this is an ad session, only events for this ad belong to the session.
|
||||
return eventMediaPeriodId.windowSequenceNumber == adMediaPeriodId.windowSequenceNumber
|
||||
&& eventMediaPeriodId.adGroupIndex == adMediaPeriodId.adGroupIndex
|
||||
&& eventMediaPeriodId.adIndexInAdGroup == adMediaPeriodId.adIndexInAdGroup;
|
||||
}
|
||||
|
||||
public void maybeSetWindowSequenceNumber(
|
||||
int eventWindowIndex, @Nullable MediaPeriodId eventMediaPeriodId) {
|
||||
if (windowSequenceNumber == C.INDEX_UNSET
|
||||
&& eventWindowIndex == windowIndex
|
||||
&& eventMediaPeriodId != null
|
||||
&& !eventMediaPeriodId.isAd()) {
|
||||
// Set window sequence number for this session as soon as we have one.
|
||||
windowSequenceNumber = eventMediaPeriodId.windowSequenceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFinishedAtEventTime(EventTime eventTime) {
|
||||
if (windowSequenceNumber == C.INDEX_UNSET) {
|
||||
// Sessions with unspecified window sequence number are kept until we know more.
|
||||
return false;
|
||||
}
|
||||
if (eventTime.mediaPeriodId == null) {
|
||||
// For event times without media period id (e.g. after seek to new window), we only keep
|
||||
// sessions of this window.
|
||||
return windowIndex != eventTime.windowIndex;
|
||||
}
|
||||
if (eventTime.mediaPeriodId.windowSequenceNumber > windowSequenceNumber) {
|
||||
// All past window sequence numbers are finished.
|
||||
return true;
|
||||
}
|
||||
if (adMediaPeriodId == null) {
|
||||
// Current or future content is not finished.
|
||||
return false;
|
||||
}
|
||||
int eventPeriodIndex = eventTime.timeline.getIndexOfPeriod(eventTime.mediaPeriodId.periodUid);
|
||||
int adPeriodIndex = eventTime.timeline.getIndexOfPeriod(adMediaPeriodId.periodUid);
|
||||
if (eventTime.mediaPeriodId.windowSequenceNumber < adMediaPeriodId.windowSequenceNumber
|
||||
|| eventPeriodIndex < adPeriodIndex) {
|
||||
// Ads in future windows or periods are not finished.
|
||||
return false;
|
||||
}
|
||||
if (eventPeriodIndex > adPeriodIndex) {
|
||||
// Ads in past periods are finished.
|
||||
return true;
|
||||
}
|
||||
if (eventTime.mediaPeriodId.isAd()) {
|
||||
int eventAdGroup = eventTime.mediaPeriodId.adGroupIndex;
|
||||
int eventAdIndex = eventTime.mediaPeriodId.adIndexInAdGroup;
|
||||
// Finished if event is for an ad after this one in the same period.
|
||||
return eventAdGroup > adMediaPeriodId.adGroupIndex
|
||||
|| (eventAdGroup == adMediaPeriodId.adGroupIndex
|
||||
&& eventAdIndex > adMediaPeriodId.adIndexInAdGroup);
|
||||
} else {
|
||||
eventTime.timeline.getPeriod(adPeriodIndex, period);
|
||||
long adGroupTimeMs =
|
||||
adMediaPeriodId.adGroupIndex < period.getAdGroupCount()
|
||||
? C.usToMs(period.getAdGroupTimeUs(adMediaPeriodId.adGroupIndex))
|
||||
: 0;
|
||||
// Finished if the event is for content after this ad.
|
||||
return adGroupTimeMs <= eventTime.currentPlaybackPositionMs;
|
||||
}
|
||||
}
|
||||
|
||||
private int resolveWindowIndexToNewTimeline(
|
||||
Timeline oldTimeline, Timeline newTimeline, int windowIndex) {
|
||||
if (windowIndex >= oldTimeline.getWindowCount()) {
|
||||
return windowIndex < newTimeline.getWindowCount() ? windowIndex : C.INDEX_UNSET;
|
||||
}
|
||||
oldTimeline.getWindow(windowIndex, window);
|
||||
for (int periodIndex = window.firstPeriodIndex;
|
||||
periodIndex <= window.lastPeriodIndex;
|
||||
periodIndex++) {
|
||||
Object periodUid = oldTimeline.getUidOfPeriod(periodIndex);
|
||||
int newPeriodIndex = newTimeline.getIndexOfPeriod(periodUid);
|
||||
if (newPeriodIndex != C.INDEX_UNSET) {
|
||||
return newTimeline.getPeriod(newPeriodIndex, period).windowIndex;
|
||||
}
|
||||
}
|
||||
return C.INDEX_UNSET;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.analytics;
|
||||
|
||||
import com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime;
|
||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
|
||||
/**
|
||||
* Manager for active playback sessions.
|
||||
*
|
||||
* <p>The manager keeps track of the association between window index and/or media period id to
|
||||
* session identifier.
|
||||
*/
|
||||
public interface PlaybackSessionManager {
|
||||
|
||||
/** A listener for session updates. */
|
||||
interface Listener {
|
||||
|
||||
/**
|
||||
* Called when a new session is created as a result of {@link #updateSessions(EventTime)}.
|
||||
*
|
||||
* @param eventTime The {@link EventTime} at which the session is created.
|
||||
* @param sessionId The identifier of the new session.
|
||||
*/
|
||||
void onSessionCreated(EventTime eventTime, String sessionId);
|
||||
|
||||
/**
|
||||
* Called when a session becomes active, i.e. playing in the foreground.
|
||||
*
|
||||
* @param eventTime The {@link EventTime} at which the session becomes active.
|
||||
* @param sessionId The identifier of the session.
|
||||
*/
|
||||
void onSessionActive(EventTime eventTime, String sessionId);
|
||||
|
||||
/**
|
||||
* Called when a session is interrupted by ad playback.
|
||||
*
|
||||
* @param eventTime The {@link EventTime} at which the ad playback starts.
|
||||
* @param contentSessionId The session identifier of the content session.
|
||||
* @param adSessionId The identifier of the ad session.
|
||||
*/
|
||||
void onAdPlaybackStarted(EventTime eventTime, String contentSessionId, String adSessionId);
|
||||
|
||||
/**
|
||||
* Called when a session is permanently finished.
|
||||
*
|
||||
* @param eventTime The {@link EventTime} at which the session finished.
|
||||
* @param sessionId The identifier of the finished session.
|
||||
* @param automaticTransitionToNextPlayback Whether the session finished because of an automatic
|
||||
* transition to the next playback item.
|
||||
*/
|
||||
void onSessionFinished(
|
||||
EventTime eventTime, String sessionId, boolean automaticTransitionToNextPlayback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to be notified of session updates. Must be called before the session manager
|
||||
* is used.
|
||||
*
|
||||
* @param listener The {@link Listener} to be notified of session updates.
|
||||
*/
|
||||
void setListener(Listener listener);
|
||||
|
||||
/**
|
||||
* Returns the session identifier for the given media period id.
|
||||
*
|
||||
* <p>Note that this will reserve a new session identifier if it doesn't exist yet, but will not
|
||||
* call any {@link Listener} callbacks.
|
||||
*
|
||||
* @param timeline The timeline, {@code mediaPeriodId} is part of.
|
||||
* @param mediaPeriodId A {@link MediaPeriodId}.
|
||||
*/
|
||||
String getSessionForMediaPeriodId(Timeline timeline, MediaPeriodId mediaPeriodId);
|
||||
|
||||
/**
|
||||
* Returns whether an event time belong to a session.
|
||||
*
|
||||
* @param eventTime The {@link EventTime}.
|
||||
* @param sessionId A session identifier.
|
||||
* @return Whether the event belongs to the specified session.
|
||||
*/
|
||||
boolean belongsToSession(EventTime eventTime, String sessionId);
|
||||
|
||||
/**
|
||||
* Updates or creates sessions based on a player {@link EventTime}.
|
||||
*
|
||||
* @param eventTime The {@link EventTime}.
|
||||
*/
|
||||
void updateSessions(EventTime eventTime);
|
||||
|
||||
/**
|
||||
* Updates the session associations to a new timeline.
|
||||
*
|
||||
* @param eventTime The event time with the timeline change.
|
||||
*/
|
||||
void handleTimelineUpdate(EventTime eventTime);
|
||||
|
||||
/**
|
||||
* Handles a position discontinuity.
|
||||
*
|
||||
* @param eventTime The event time of the position discontinuity.
|
||||
* @param reason The {@link DiscontinuityReason}.
|
||||
*/
|
||||
void handlePositionDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason);
|
||||
}
|
@ -0,0 +1,957 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
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.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime;
|
||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/** Unit test for {@link DefaultPlaybackSessionManager}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class DefaultPlaybackSessionManagerTest {
|
||||
|
||||
private DefaultPlaybackSessionManager sessionManager;
|
||||
|
||||
@Mock private PlaybackSessionManager.Listener mockListener;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
sessionManager = new DefaultPlaybackSessionManager();
|
||||
sessionManager.setListener(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_withoutMediaPeriodId_createsNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
EventTime eventTime = createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId */ null);
|
||||
|
||||
sessionManager.updateSessions(eventTime);
|
||||
|
||||
verify(mockListener).onSessionCreated(eq(eventTime), anyString());
|
||||
verify(mockListener).onSessionActive(eq(eventTime), anyString());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_withMediaPeriodId_createsNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId);
|
||||
|
||||
sessionManager.updateSessions(eventTime);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime), sessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime, sessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId))
|
||||
.isEqualTo(sessionId.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
updateSessions_ofSameWindow_withMediaPeriodId_afterWithoutMediaPeriodId_doesNotCreateNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime1 =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId))
|
||||
.isEqualTo(sessionId.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_ofSameWindow_withAd_afterWithoutMediaPeriodId_createsNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime1 =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> contentSessionId = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> adSessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), contentSessionId.capture());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), adSessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, contentSessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(contentSessionId).isNotEqualTo(adSessionId);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId))
|
||||
.isEqualTo(adSessionId.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
updateSessions_ofSameWindow_withoutMediaPeriodId_afterMediaPeriodId_doesNotCreateNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId);
|
||||
EventTime eventTime2 =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId))
|
||||
.isEqualTo(sessionId.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_ofSameWindow_withoutMediaPeriodId_afterAd_doesNotCreateNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId);
|
||||
EventTime eventTime2 =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId))
|
||||
.isEqualTo(sessionId.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_withOtherMediaPeriodId_ofSameWindow_doesNotCreateNewSession() {
|
||||
Timeline timeline =
|
||||
new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 0));
|
||||
MediaPeriodId mediaPeriodId1 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
MediaPeriodId mediaPeriodId2 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId1);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId2);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId1))
|
||||
.isEqualTo(sessionId.getValue());
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId2))
|
||||
.isEqualTo(sessionId.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_withAd_ofSameWindow_createsNewSession() {
|
||||
Timeline timeline =
|
||||
new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 0));
|
||||
MediaPeriodId mediaPeriodId1 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
MediaPeriodId mediaPeriodId2 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId1);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId2);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> contentSessionId = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> adSessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), contentSessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, contentSessionId.getValue());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), adSessionId.capture());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(contentSessionId).isNotEqualTo(adSessionId);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId1))
|
||||
.isEqualTo(contentSessionId.getValue());
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId2))
|
||||
.isEqualTo(adSessionId.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_ofOtherWindow_createsNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
|
||||
EventTime eventTime1 =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
EventTime eventTime2 =
|
||||
createEventTime(timeline, /* windowIndex= */ 1, /* mediaPeriodId= */ null);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> sessionId2 = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId1.capture());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), sessionId2.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId1.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionId1).isNotEqualTo(sessionId2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_withMediaPeriodId_ofOtherWindow_createsNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
|
||||
MediaPeriodId mediaPeriodId1 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
MediaPeriodId mediaPeriodId2 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId1);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId2);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> sessionId2 = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId1.capture());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), sessionId2.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId1.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionId1).isNotEqualTo(sessionId2);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId1))
|
||||
.isEqualTo(sessionId1.getValue());
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId2))
|
||||
.isEqualTo(sessionId2.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_ofSameWindow_withNewWindowSequenceNumber_createsNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId1 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
MediaPeriodId mediaPeriodId2 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId1);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId2);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> sessionId2 = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId1.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId1.getValue());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), sessionId2.capture());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionId1).isNotEqualTo(sessionId2);
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId1))
|
||||
.isEqualTo(sessionId1.getValue());
|
||||
assertThat(sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId2))
|
||||
.isEqualTo(sessionId2.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
updateSessions_withoutMediaPeriodId_andPreviouslyCreatedSessions_doesNotCreateNewSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId1 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
MediaPeriodId mediaPeriodId2 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1);
|
||||
MediaPeriodId mediaPeriodIdWithAd =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId1);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId2);
|
||||
EventTime eventTime3 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodIdWithAd);
|
||||
EventTime eventTime4 =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
sessionManager.updateSessions(eventTime3);
|
||||
sessionManager.updateSessions(eventTime4);
|
||||
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), anyString());
|
||||
verify(mockListener).onSessionActive(eq(eventTime1), anyString());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), anyString());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime3), anyString());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionForMediaPeriodId_returnsValue_butDoesNotCreateSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
String session = sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId);
|
||||
|
||||
assertThat(session).isNotEmpty();
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_afterSessionForMediaPeriodId_withSameMediaPeriodId_returnsSameValue() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId);
|
||||
|
||||
String expectedSessionId = sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId);
|
||||
sessionManager.updateSessions(eventTime);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime), sessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime, sessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionId.getValue()).isEqualTo(expectedSessionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSessions_withoutMediaPeriodId_afterSessionForMediaPeriodId_returnsSameValue() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
EventTime eventTime =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
|
||||
String expectedSessionId = sessionManager.getSessionForMediaPeriodId(timeline, mediaPeriodId);
|
||||
sessionManager.updateSessions(eventTime);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime), sessionId.capture());
|
||||
verify(mockListener).onSessionActive(eventTime, sessionId.getValue());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(sessionId.getValue()).isEqualTo(expectedSessionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void belongsToSession_withSameWindowIndex_returnsTrue() {
|
||||
EventTime eventTime =
|
||||
createEventTime(Timeline.EMPTY, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
EventTime eventTimeWithTimeline =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
EventTime eventTimeWithMediaPeriodId =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId);
|
||||
sessionManager.updateSessions(eventTime);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime), sessionId.capture());
|
||||
assertThat(sessionManager.belongsToSession(eventTime, sessionId.getValue())).isTrue();
|
||||
assertThat(sessionManager.belongsToSession(eventTimeWithTimeline, sessionId.getValue()))
|
||||
.isTrue();
|
||||
assertThat(sessionManager.belongsToSession(eventTimeWithMediaPeriodId, sessionId.getValue()))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void belongsToSession_withOtherWindowIndex_returnsFalse() {
|
||||
EventTime eventTime =
|
||||
createEventTime(Timeline.EMPTY, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
EventTime eventTimeOtherWindow =
|
||||
createEventTime(Timeline.EMPTY, /* windowIndex= */ 1, /* mediaPeriodId= */ null);
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
|
||||
MediaPeriodId mediaPeriodId =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1);
|
||||
EventTime eventTimeWithOtherMediaPeriodId =
|
||||
createEventTime(timeline, /* windowIndex= */ 1, mediaPeriodId);
|
||||
sessionManager.updateSessions(eventTime);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime), sessionId.capture());
|
||||
assertThat(sessionManager.belongsToSession(eventTimeOtherWindow, sessionId.getValue()))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
sessionManager.belongsToSession(eventTimeWithOtherMediaPeriodId, sessionId.getValue()))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void belongsToSession_withOtherWindowSequenceNumber_returnsFalse() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId1 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
MediaPeriodId mediaPeriodId2 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId1);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId2);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
|
||||
ArgumentCaptor<String> sessionId = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId.capture());
|
||||
assertThat(sessionManager.belongsToSession(eventTime2, sessionId.getValue())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void belongsToSession_withAd_returnsFalse() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
MediaPeriodId mediaPeriodId1 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
|
||||
MediaPeriodId mediaPeriodId2 =
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 1);
|
||||
EventTime eventTime1 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId1);
|
||||
EventTime eventTime2 = createEventTime(timeline, /* windowIndex= */ 0, mediaPeriodId2);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
ArgumentCaptor<String> sessionId1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> sessionId2 = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId1.capture());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), sessionId2.capture());
|
||||
assertThat(sessionManager.belongsToSession(eventTime2, sessionId1.getValue())).isFalse();
|
||||
assertThat(sessionManager.belongsToSession(eventTime1, sessionId2.getValue())).isFalse();
|
||||
assertThat(sessionManager.belongsToSession(eventTime2, sessionId2.getValue())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initialTimelineUpdate_finishesAllSessionsOutsideTimeline() {
|
||||
EventTime eventTime1 =
|
||||
createEventTime(Timeline.EMPTY, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
EventTime eventTime2 =
|
||||
createEventTime(Timeline.EMPTY, /* windowIndex= */ 1, /* mediaPeriodId= */ null);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
EventTime newTimelineEventTime =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
|
||||
sessionManager.handleTimelineUpdate(newTimelineEventTime);
|
||||
|
||||
ArgumentCaptor<String> sessionId1 = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<String> sessionId2 = ArgumentCaptor.forClass(String.class);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId1.capture());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), sessionId2.capture());
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId1.getValue());
|
||||
verify(mockListener)
|
||||
.onSessionFinished(
|
||||
newTimelineEventTime,
|
||||
sessionId2.getValue(),
|
||||
/* automaticTransitionToNextPlayback= */ false);
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicTimelineUpdate_resolvesWindowIndices() {
|
||||
Timeline initialTimeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 100),
|
||||
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 200),
|
||||
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 300));
|
||||
EventTime eventForInitialTimelineId100 =
|
||||
createEventTime(
|
||||
initialTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
initialTimeline.getUidOfPeriod(/* periodIndex= */ 1),
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime eventForInitialTimelineId200 =
|
||||
createEventTime(
|
||||
initialTimeline,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaPeriodId(
|
||||
initialTimeline.getUidOfPeriod(/* periodIndex= */ 2),
|
||||
/* windowSequenceNumber= */ 1));
|
||||
EventTime eventForInitialTimelineId300 =
|
||||
createEventTime(
|
||||
initialTimeline,
|
||||
/* windowIndex= */ 2,
|
||||
new MediaPeriodId(
|
||||
initialTimeline.getUidOfPeriod(/* periodIndex= */ 3),
|
||||
/* windowSequenceNumber= */ 2));
|
||||
sessionManager.handleTimelineUpdate(eventForInitialTimelineId100);
|
||||
sessionManager.updateSessions(eventForInitialTimelineId100);
|
||||
sessionManager.updateSessions(eventForInitialTimelineId200);
|
||||
sessionManager.updateSessions(eventForInitialTimelineId300);
|
||||
String sessionId100 =
|
||||
sessionManager.getSessionForMediaPeriodId(
|
||||
initialTimeline, eventForInitialTimelineId100.mediaPeriodId);
|
||||
String sessionId200 =
|
||||
sessionManager.getSessionForMediaPeriodId(
|
||||
initialTimeline, eventForInitialTimelineId200.mediaPeriodId);
|
||||
String sessionId300 =
|
||||
sessionManager.getSessionForMediaPeriodId(
|
||||
initialTimeline, eventForInitialTimelineId300.mediaPeriodId);
|
||||
|
||||
Timeline timelineUpdate =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 300),
|
||||
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 100));
|
||||
EventTime eventForTimelineUpdateId100 =
|
||||
createEventTime(
|
||||
timelineUpdate,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaPeriodId(
|
||||
timelineUpdate.getUidOfPeriod(/* periodIndex= */ 1),
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime eventForTimelineUpdateId300 =
|
||||
createEventTime(
|
||||
timelineUpdate,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
timelineUpdate.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* windowSequenceNumber= */ 2));
|
||||
|
||||
sessionManager.handleTimelineUpdate(eventForTimelineUpdateId100);
|
||||
String updatedSessionId100 =
|
||||
sessionManager.getSessionForMediaPeriodId(
|
||||
timelineUpdate, eventForTimelineUpdateId100.mediaPeriodId);
|
||||
String updatedSessionId300 =
|
||||
sessionManager.getSessionForMediaPeriodId(
|
||||
timelineUpdate, eventForTimelineUpdateId300.mediaPeriodId);
|
||||
|
||||
verify(mockListener).onSessionCreated(eventForInitialTimelineId100, sessionId100);
|
||||
verify(mockListener).onSessionActive(eventForInitialTimelineId100, sessionId100);
|
||||
verify(mockListener).onSessionCreated(eventForInitialTimelineId200, sessionId200);
|
||||
verify(mockListener).onSessionCreated(eventForInitialTimelineId300, sessionId300);
|
||||
verify(mockListener)
|
||||
.onSessionFinished(
|
||||
eventForTimelineUpdateId100,
|
||||
sessionId200,
|
||||
/* automaticTransitionToNextPlayback= */ false);
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
assertThat(updatedSessionId100).isEqualTo(sessionId100);
|
||||
assertThat(updatedSessionId300).isEqualTo(sessionId300);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_withinWindow_doesNotFinishSession() {
|
||||
Timeline timeline =
|
||||
new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 100));
|
||||
EventTime eventTime1 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
|
||||
EventTime eventTime2 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
|
||||
sessionManager.handleTimelineUpdate(eventTime1);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(
|
||||
eventTime2, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
|
||||
|
||||
verify(mockListener).onSessionCreated(eq(eventTime1), anyString());
|
||||
verify(mockListener).onSessionActive(eq(eventTime1), anyString());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_toNewWindow_withPeriodTransitionReason_finishesSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
|
||||
EventTime eventTime1 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
|
||||
EventTime eventTime2 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
|
||||
sessionManager.handleTimelineUpdate(eventTime1);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
String sessionId1 =
|
||||
sessionManager.getSessionForMediaPeriodId(timeline, eventTime1.mediaPeriodId);
|
||||
String sessionId2 =
|
||||
sessionManager.getSessionForMediaPeriodId(timeline, eventTime2.mediaPeriodId);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(
|
||||
eventTime2, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
|
||||
|
||||
verify(mockListener).onSessionCreated(eventTime1, sessionId1);
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId1);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), anyString());
|
||||
verify(mockListener)
|
||||
.onSessionFinished(eventTime2, sessionId1, /* automaticTransitionToNextPlayback= */ true);
|
||||
verify(mockListener).onSessionActive(eventTime2, sessionId2);
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_toNewWindow_withSeekTransitionReason_finishesSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
|
||||
EventTime eventTime1 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
|
||||
EventTime eventTime2 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
|
||||
sessionManager.handleTimelineUpdate(eventTime1);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
String sessionId1 =
|
||||
sessionManager.getSessionForMediaPeriodId(timeline, eventTime1.mediaPeriodId);
|
||||
String sessionId2 =
|
||||
sessionManager.getSessionForMediaPeriodId(timeline, eventTime2.mediaPeriodId);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(eventTime2, Player.DISCONTINUITY_REASON_SEEK);
|
||||
|
||||
verify(mockListener).onSessionCreated(eventTime1, sessionId1);
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId1);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime2), anyString());
|
||||
verify(mockListener)
|
||||
.onSessionFinished(eventTime2, sessionId1, /* automaticTransitionToNextPlayback= */ false);
|
||||
verify(mockListener).onSessionActive(eventTime2, sessionId2);
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_toSameWindow_withoutMediaPeriodId_doesNotFinishSession() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
EventTime eventTime1 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
|
||||
EventTime eventTime2 =
|
||||
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||
sessionManager.handleTimelineUpdate(eventTime1);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(eventTime2, Player.DISCONTINUITY_REASON_SEEK);
|
||||
|
||||
verify(mockListener, never()).onSessionFinished(any(), anyString(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_toNewWindow_finishesOnlyPastSessions() {
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 4);
|
||||
EventTime eventTime1 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
|
||||
EventTime eventTime2 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
|
||||
EventTime eventTime3 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 2,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 2), /* windowSequenceNumber= */ 2));
|
||||
EventTime eventTime4 =
|
||||
createEventTime(
|
||||
timeline,
|
||||
/* windowIndex= */ 3,
|
||||
new MediaPeriodId(
|
||||
timeline.getUidOfPeriod(/* periodIndex= */ 3), /* windowSequenceNumber= */ 3));
|
||||
sessionManager.handleTimelineUpdate(eventTime1);
|
||||
sessionManager.updateSessions(eventTime1);
|
||||
sessionManager.updateSessions(eventTime2);
|
||||
sessionManager.updateSessions(eventTime3);
|
||||
sessionManager.updateSessions(eventTime4);
|
||||
String sessionId1 =
|
||||
sessionManager.getSessionForMediaPeriodId(timeline, eventTime1.mediaPeriodId);
|
||||
String sessionId2 =
|
||||
sessionManager.getSessionForMediaPeriodId(timeline, eventTime2.mediaPeriodId);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(eventTime3, Player.DISCONTINUITY_REASON_SEEK);
|
||||
|
||||
verify(mockListener).onSessionCreated(eventTime1, sessionId1);
|
||||
verify(mockListener).onSessionActive(eventTime1, sessionId1);
|
||||
verify(mockListener).onSessionCreated(eventTime2, sessionId2);
|
||||
verify(mockListener).onSessionCreated(eq(eventTime3), anyString());
|
||||
verify(mockListener).onSessionCreated(eq(eventTime4), anyString());
|
||||
verify(mockListener)
|
||||
.onSessionFinished(eventTime3, sessionId1, /* automaticTransitionToNextPlayback= */ false);
|
||||
verify(mockListener)
|
||||
.onSessionFinished(eventTime3, sessionId2, /* automaticTransitionToNextPlayback= */ false);
|
||||
verify(mockListener).onSessionActive(eq(eventTime3), anyString());
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_fromAdToContent_finishesAd() {
|
||||
Timeline adTimeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* periodCount= */ 1,
|
||||
/* id= */ 0,
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs =*/ 10 * C.MICROS_PER_SECOND,
|
||||
new AdPlaybackState(/* adGroupTimesUs= */ 0, 5 * C.MICROS_PER_SECOND)
|
||||
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
|
||||
.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1)));
|
||||
EventTime adEventTime1 =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime adEventTime2 =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 1,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime contentEventTime =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* windowSequenceNumber= */ 0,
|
||||
/* nextAdGroupIndex= */ 1));
|
||||
sessionManager.handleTimelineUpdate(adEventTime1);
|
||||
sessionManager.updateSessions(adEventTime1);
|
||||
sessionManager.updateSessions(adEventTime2);
|
||||
String adSessionId1 =
|
||||
sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime1.mediaPeriodId);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(
|
||||
contentEventTime, Player.DISCONTINUITY_REASON_AD_INSERTION);
|
||||
|
||||
verify(mockListener).onSessionCreated(adEventTime1, adSessionId1);
|
||||
verify(mockListener).onSessionActive(adEventTime1, adSessionId1);
|
||||
verify(mockListener).onSessionCreated(eq(adEventTime2), anyString());
|
||||
verify(mockListener)
|
||||
.onSessionFinished(
|
||||
contentEventTime, adSessionId1, /* automaticTransitionToNextPlayback= */ true);
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_fromContentToAd_doesNotFinishSessions() {
|
||||
Timeline adTimeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* periodCount= */ 1,
|
||||
/* id= */ 0,
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs =*/ 10 * C.MICROS_PER_SECOND,
|
||||
new AdPlaybackState(
|
||||
/* adGroupTimesUs= */ 2 * C.MICROS_PER_SECOND, 5 * C.MICROS_PER_SECOND)
|
||||
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
|
||||
.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1)));
|
||||
EventTime adEventTime1 =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime adEventTime2 =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 1,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime contentEventTime =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* windowSequenceNumber= */ 0,
|
||||
/* nextAdGroupIndex= */ 0));
|
||||
sessionManager.handleTimelineUpdate(contentEventTime);
|
||||
sessionManager.updateSessions(contentEventTime);
|
||||
sessionManager.updateSessions(adEventTime1);
|
||||
sessionManager.updateSessions(adEventTime2);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(
|
||||
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION);
|
||||
|
||||
verify(mockListener, never()).onSessionFinished(any(), anyString(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionDiscontinuity_fromAdToAd_finishesPastAds_andNotifiesAdPlaybackStated() {
|
||||
Timeline adTimeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* periodCount= */ 1,
|
||||
/* id= */ 0,
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs =*/ 10 * C.MICROS_PER_SECOND,
|
||||
new AdPlaybackState(/* adGroupTimesUs= */ 0, 5 * C.MICROS_PER_SECOND)
|
||||
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
|
||||
.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1)));
|
||||
EventTime adEventTime1 =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime adEventTime2 =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* adGroupIndex= */ 1,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* windowSequenceNumber= */ 0));
|
||||
EventTime contentEventTime =
|
||||
createEventTime(
|
||||
adTimeline,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaPeriodId(
|
||||
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
|
||||
/* windowSequenceNumber= */ 0,
|
||||
/* nextAdGroupIndex= */ 1));
|
||||
sessionManager.handleTimelineUpdate(contentEventTime);
|
||||
sessionManager.updateSessions(contentEventTime);
|
||||
sessionManager.updateSessions(adEventTime1);
|
||||
sessionManager.updateSessions(adEventTime2);
|
||||
String contentSessionId =
|
||||
sessionManager.getSessionForMediaPeriodId(adTimeline, contentEventTime.mediaPeriodId);
|
||||
String adSessionId1 =
|
||||
sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime1.mediaPeriodId);
|
||||
String adSessionId2 =
|
||||
sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime2.mediaPeriodId);
|
||||
|
||||
sessionManager.handlePositionDiscontinuity(
|
||||
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION);
|
||||
sessionManager.handlePositionDiscontinuity(adEventTime2, Player.DISCONTINUITY_REASON_SEEK);
|
||||
|
||||
verify(mockListener).onSessionCreated(eq(contentEventTime), anyString());
|
||||
verify(mockListener).onSessionActive(eq(contentEventTime), anyString());
|
||||
verify(mockListener).onSessionCreated(adEventTime1, adSessionId1);
|
||||
verify(mockListener).onSessionCreated(adEventTime2, adSessionId2);
|
||||
verify(mockListener).onAdPlaybackStarted(adEventTime1, contentSessionId, adSessionId1);
|
||||
verify(mockListener).onSessionActive(adEventTime1, adSessionId1);
|
||||
verify(mockListener)
|
||||
.onSessionFinished(
|
||||
adEventTime2, adSessionId1, /* automaticTransitionToNextPlayback= */ false);
|
||||
verify(mockListener).onAdPlaybackStarted(adEventTime2, contentSessionId, adSessionId2);
|
||||
verify(mockListener).onSessionActive(adEventTime2, adSessionId2);
|
||||
verifyNoMoreInteractions(mockListener);
|
||||
}
|
||||
|
||||
private static EventTime createEventTime(
|
||||
Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
|
||||
return new EventTime(
|
||||
/* realtimeMs = */ 0,
|
||||
timeline,
|
||||
windowIndex,
|
||||
mediaPeriodId,
|
||||
/* eventPlaybackPositionMs= */ 0,
|
||||
/* currentPlaybackPositionMs= */ 0,
|
||||
/* totalBufferedDurationMs= */ 0);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user