Commit bb787d66 by tonihei Committed by Oliver Woodman

Ensure onAdPlaybackStarted is only called after the session is created

We currently try to call onAdPlaybackStarted even if the ad session
is not created yet and if not, we never call the callback afterwards.

Make sure to update and create the current session before trying to
send onAdPlaybackStarted.

As a result, we can merge updateSessions into the existing
handleTimelineChanged and handleDiscontinuity calls as they always
need to be called together.

PiperOrigin-RevId: 321383860
parent 8cc3cc4e
......@@ -36,6 +36,7 @@ import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.common.base.Objects;
import java.io.IOException;
/**
......@@ -155,6 +156,42 @@ public interface AnalyticsListener {
this.currentPlaybackPositionMs = currentPlaybackPositionMs;
this.totalBufferedDurationMs = totalBufferedDurationMs;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EventTime eventTime = (EventTime) o;
return realtimeMs == eventTime.realtimeMs
&& windowIndex == eventTime.windowIndex
&& eventPlaybackPositionMs == eventTime.eventPlaybackPositionMs
&& currentWindowIndex == eventTime.currentWindowIndex
&& currentPlaybackPositionMs == eventTime.currentPlaybackPositionMs
&& totalBufferedDurationMs == eventTime.totalBufferedDurationMs
&& Objects.equal(timeline, eventTime.timeline)
&& Objects.equal(mediaPeriodId, eventTime.mediaPeriodId)
&& Objects.equal(currentTimeline, eventTime.currentTimeline)
&& Objects.equal(currentMediaPeriodId, eventTime.currentMediaPeriodId);
}
@Override
public int hashCode() {
return Objects.hashCode(
realtimeMs,
timeline,
windowIndex,
mediaPeriodId,
eventPlaybackPositionMs,
currentTimeline,
currentWindowIndex,
currentMediaPeriodId,
currentPlaybackPositionMs,
totalBufferedDurationMs);
}
}
/**
......
......@@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer2.analytics;
import static com.google.android.exoplayer2.C.usToMs;
import static java.lang.Math.max;
import android.util.Base64;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
......@@ -120,6 +123,38 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
if (currentSessionId == null) {
currentSessionId = eventSession.sessionId;
}
if (eventTime.mediaPeriodId != null && eventTime.mediaPeriodId.isAd()) {
// Ensure that the content session for an ad session is created first.
MediaPeriodId contentMediaPeriodId =
new MediaPeriodId(
eventTime.mediaPeriodId.periodUid,
eventTime.mediaPeriodId.windowSequenceNumber,
eventTime.mediaPeriodId.adGroupIndex);
SessionDescriptor contentSession =
getOrAddSession(eventTime.windowIndex, contentMediaPeriodId);
if (!contentSession.isCreated) {
contentSession.isCreated = true;
eventTime.timeline.getPeriodByUid(eventTime.mediaPeriodId.periodUid, period);
long adGroupPositionMs =
usToMs(period.getAdGroupTimeUs(eventTime.mediaPeriodId.adGroupIndex))
+ period.getPositionInWindowMs();
// getAdGroupTimeUs may return 0 for prerolls despite period offset.
adGroupPositionMs = max(0, adGroupPositionMs);
EventTime eventTimeForContent =
new EventTime(
eventTime.realtimeMs,
eventTime.timeline,
eventTime.windowIndex,
contentMediaPeriodId,
/* eventPlaybackPositionMs= */ adGroupPositionMs,
eventTime.currentTimeline,
eventTime.currentWindowIndex,
eventTime.currentMediaPeriodId,
eventTime.currentPlaybackPositionMs,
eventTime.totalBufferedDurationMs);
listener.onSessionCreated(eventTimeForContent, contentSession.sessionId);
}
}
if (!eventSession.isCreated) {
eventSession.isCreated = true;
listener.onSessionCreated(eventTime, eventSession.sessionId);
......@@ -131,7 +166,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
}
@Override
public synchronized void handleTimelineUpdate(EventTime eventTime) {
public synchronized void updateSessionsWithTimelineChange(EventTime eventTime) {
Assertions.checkNotNull(listener);
Timeline previousTimeline = currentTimeline;
currentTimeline = eventTime.timeline;
......@@ -149,11 +184,11 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
}
}
}
handlePositionDiscontinuity(eventTime, Player.DISCONTINUITY_REASON_INTERNAL);
updateSessionsWithDiscontinuity(eventTime, Player.DISCONTINUITY_REASON_INTERNAL);
}
@Override
public synchronized void handlePositionDiscontinuity(
public synchronized void updateSessionsWithDiscontinuity(
EventTime eventTime, @DiscontinuityReason int reason) {
Assertions.checkNotNull(listener);
boolean hasAutomaticTransition =
......@@ -179,6 +214,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
SessionDescriptor currentSessionDescriptor =
getOrAddSession(eventTime.windowIndex, eventTime.mediaPeriodId);
currentSessionId = currentSessionDescriptor.sessionId;
updateSessions(eventTime);
if (eventTime.mediaPeriodId != null
&& eventTime.mediaPeriodId.isAd()
&& (previousSessionDescriptor == null
......@@ -195,12 +231,10 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
eventTime.mediaPeriodId.periodUid, eventTime.mediaPeriodId.windowSequenceNumber);
SessionDescriptor contentSession =
getOrAddSession(eventTime.windowIndex, contentMediaPeriodId);
if (contentSession.isCreated && currentSessionDescriptor.isCreated) {
listener.onAdPlaybackStarted(
eventTime, contentSession.sessionId, currentSessionDescriptor.sessionId);
}
}
}
@Override
public void finishAllSessions(EventTime eventTime) {
......
......@@ -99,24 +99,34 @@ public interface PlaybackSessionManager {
/**
* Updates or creates sessions based on a player {@link EventTime}.
*
* <p>Call {@link #updateSessionsWithTimelineChange(EventTime)} or {@link
* #updateSessionsWithDiscontinuity(EventTime, int)} if the event is a {@link Timeline} change or
* a position discontinuity respectively.
*
* @param eventTime The {@link EventTime}.
*/
void updateSessions(EventTime eventTime);
/**
* Updates the session associations to a new timeline.
* Updates or creates sessions based on a {@link Timeline} change at {@link EventTime}.
*
* <p>Should be called instead of {@link #updateSessions(EventTime)} if a {@link Timeline} change
* occurred.
*
* @param eventTime The event time with the timeline change.
* @param eventTime The {@link EventTime} with the timeline change.
*/
void handleTimelineUpdate(EventTime eventTime);
void updateSessionsWithTimelineChange(EventTime eventTime);
/**
* Handles a position discontinuity.
* Updates or creates sessions based on a position discontinuity at {@link EventTime}.
*
* <p>Should be called instead of {@link #updateSessions(EventTime)} if a position discontinuity
* occurred.
*
* @param eventTime The event time of the position discontinuity.
* @param eventTime The {@link EventTime} of the position discontinuity.
* @param reason The {@link DiscontinuityReason}.
*/
void handlePositionDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason);
void updateSessionsWithDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason);
/**
* Finishes all existing sessions and calls their respective {@link
......
......@@ -284,8 +284,7 @@ public final class PlaybackStatsListener
@Override
public void onTimelineChanged(EventTime eventTime, @Player.TimelineChangeReason int reason) {
sessionManager.handleTimelineUpdate(eventTime);
maybeAddSession(eventTime);
sessionManager.updateSessionsWithTimelineChange(eventTime);
for (String session : playbackStatsTrackers.keySet()) {
if (sessionManager.belongsToSession(eventTime, session)) {
playbackStatsTrackers.get(session).onPositionDiscontinuity(eventTime, /* isSeek= */ false);
......@@ -295,8 +294,10 @@ public final class PlaybackStatsListener
@Override
public void onPositionDiscontinuity(EventTime eventTime, @Player.DiscontinuityReason int reason) {
sessionManager.handlePositionDiscontinuity(eventTime, reason);
maybeAddSession(eventTime);
boolean isCompletelyIdle = eventTime.timeline.isEmpty() && playbackState == Player.STATE_IDLE;
if (!isCompletelyIdle) {
sessionManager.updateSessionsWithDiscontinuity(eventTime, reason);
}
if (reason == Player.DISCONTINUITY_REASON_SEEK) {
onSeekStartedCalled = false;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment