Remove flakiness from DefaultAnalyticsCollectorTest
Our FakeClock generally makes sure that playback tests are fully deterministic. However, this fails if the test uses blocking waits with clock.onThreadBlocked and where relevant Handlers are created without using the clock. To fix the flakiness, we can make the following adjustments: - Use TestExoPlayerBuilder instead of legacy ExoPlayerTestRunner to avoid onThreadBlocked calls. This also makes the tests more readable. - Use clock to create Handler for FakeVideoRenderer and FakeAudioRenderer. Ideally, this should be passed through RenderersFactory, but it's too disruptive given this is a public API. - Use clock for MediaSourceList and MediaPeriodQueue update handler. PiperOrigin-RevId: 490907495
This commit is contained in:
parent
fed53362c9
commit
6abc94a8b7
@ -281,7 +281,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
deliverPendingMessageAtStartPositionRequired = true;
|
deliverPendingMessageAtStartPositionRequired = true;
|
||||||
|
|
||||||
Handler eventHandler = new Handler(applicationLooper);
|
HandlerWrapper eventHandler = clock.createHandler(applicationLooper, /* callback= */ null);
|
||||||
queue = new MediaPeriodQueue(analyticsCollector, eventHandler);
|
queue = new MediaPeriodQueue(analyticsCollector, eventHandler);
|
||||||
mediaSourceList =
|
mediaSourceList =
|
||||||
new MediaSourceList(/* listener= */ this, analyticsCollector, eventHandler, playerId);
|
new MediaSourceList(/* listener= */ this, analyticsCollector, eventHandler, playerId);
|
||||||
|
@ -26,6 +26,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Player.RepeatMode;
|
import androidx.media3.common.Player.RepeatMode;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
@ -71,7 +72,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
private final Timeline.Window window;
|
private final Timeline.Window window;
|
||||||
private final AnalyticsCollector analyticsCollector;
|
private final AnalyticsCollector analyticsCollector;
|
||||||
private final Handler analyticsCollectorHandler;
|
private final HandlerWrapper analyticsCollectorHandler;
|
||||||
|
|
||||||
private long nextWindowSequenceNumber;
|
private long nextWindowSequenceNumber;
|
||||||
private @RepeatMode int repeatMode;
|
private @RepeatMode int repeatMode;
|
||||||
@ -91,7 +92,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
* on.
|
* on.
|
||||||
*/
|
*/
|
||||||
public MediaPeriodQueue(
|
public MediaPeriodQueue(
|
||||||
AnalyticsCollector analyticsCollector, Handler analyticsCollectorHandler) {
|
AnalyticsCollector analyticsCollector, HandlerWrapper analyticsCollectorHandler) {
|
||||||
this.analyticsCollector = analyticsCollector;
|
this.analyticsCollector = analyticsCollector;
|
||||||
this.analyticsCollectorHandler = analyticsCollectorHandler;
|
this.analyticsCollectorHandler = analyticsCollectorHandler;
|
||||||
period = new Timeline.Period();
|
period = new Timeline.Period();
|
||||||
|
@ -15,13 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer;
|
package androidx.media3.exoplayer;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.util.Pair;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
@ -48,6 +51,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
|
* Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
|
||||||
@ -77,11 +81,10 @@ import java.util.Set;
|
|||||||
private final IdentityHashMap<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
|
private final IdentityHashMap<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
|
||||||
private final Map<Object, MediaSourceHolder> mediaSourceByUid;
|
private final Map<Object, MediaSourceHolder> mediaSourceByUid;
|
||||||
private final MediaSourceListInfoRefreshListener mediaSourceListInfoListener;
|
private final MediaSourceListInfoRefreshListener mediaSourceListInfoListener;
|
||||||
private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher;
|
|
||||||
private final DrmSessionEventListener.EventDispatcher drmEventDispatcher;
|
|
||||||
private final HashMap<MediaSourceList.MediaSourceHolder, MediaSourceAndListener> childSources;
|
private final HashMap<MediaSourceList.MediaSourceHolder, MediaSourceAndListener> childSources;
|
||||||
private final Set<MediaSourceHolder> enabledMediaSourceHolders;
|
private final Set<MediaSourceHolder> enabledMediaSourceHolders;
|
||||||
|
private final AnalyticsCollector eventListener;
|
||||||
|
private final HandlerWrapper eventHandler;
|
||||||
private ShuffleOrder shuffleOrder;
|
private ShuffleOrder shuffleOrder;
|
||||||
private boolean isPrepared;
|
private boolean isPrepared;
|
||||||
|
|
||||||
@ -101,7 +104,7 @@ import java.util.Set;
|
|||||||
public MediaSourceList(
|
public MediaSourceList(
|
||||||
MediaSourceListInfoRefreshListener listener,
|
MediaSourceListInfoRefreshListener listener,
|
||||||
AnalyticsCollector analyticsCollector,
|
AnalyticsCollector analyticsCollector,
|
||||||
Handler analyticsCollectorHandler,
|
HandlerWrapper analyticsCollectorHandler,
|
||||||
PlayerId playerId) {
|
PlayerId playerId) {
|
||||||
this.playerId = playerId;
|
this.playerId = playerId;
|
||||||
mediaSourceListInfoListener = listener;
|
mediaSourceListInfoListener = listener;
|
||||||
@ -109,12 +112,10 @@ import java.util.Set;
|
|||||||
mediaSourceByMediaPeriod = new IdentityHashMap<>();
|
mediaSourceByMediaPeriod = new IdentityHashMap<>();
|
||||||
mediaSourceByUid = new HashMap<>();
|
mediaSourceByUid = new HashMap<>();
|
||||||
mediaSourceHolders = new ArrayList<>();
|
mediaSourceHolders = new ArrayList<>();
|
||||||
mediaSourceEventDispatcher = new MediaSourceEventListener.EventDispatcher();
|
eventListener = analyticsCollector;
|
||||||
drmEventDispatcher = new DrmSessionEventListener.EventDispatcher();
|
eventHandler = analyticsCollectorHandler;
|
||||||
childSources = new HashMap<>();
|
childSources = new HashMap<>();
|
||||||
enabledMediaSourceHolders = new HashSet<>();
|
enabledMediaSourceHolders = new HashSet<>();
|
||||||
mediaSourceEventDispatcher.addEventListener(analyticsCollectorHandler, analyticsCollector);
|
|
||||||
drmEventDispatcher.addEventListener(analyticsCollectorHandler, analyticsCollector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -308,7 +309,7 @@ import java.util.Set;
|
|||||||
Object mediaSourceHolderUid = getMediaSourceHolderUid(id.periodUid);
|
Object mediaSourceHolderUid = getMediaSourceHolderUid(id.periodUid);
|
||||||
MediaSource.MediaPeriodId childMediaPeriodId =
|
MediaSource.MediaPeriodId childMediaPeriodId =
|
||||||
id.copyWithPeriodUid(getChildPeriodUid(id.periodUid));
|
id.copyWithPeriodUid(getChildPeriodUid(id.periodUid));
|
||||||
MediaSourceHolder holder = Assertions.checkNotNull(mediaSourceByUid.get(mediaSourceHolderUid));
|
MediaSourceHolder holder = checkNotNull(mediaSourceByUid.get(mediaSourceHolderUid));
|
||||||
enableMediaSource(holder);
|
enableMediaSource(holder);
|
||||||
holder.activeMediaPeriodIds.add(childMediaPeriodId);
|
holder.activeMediaPeriodIds.add(childMediaPeriodId);
|
||||||
MediaPeriod mediaPeriod =
|
MediaPeriod mediaPeriod =
|
||||||
@ -324,8 +325,7 @@ import java.util.Set;
|
|||||||
* @param mediaPeriod The period to release.
|
* @param mediaPeriod The period to release.
|
||||||
*/
|
*/
|
||||||
public void releasePeriod(MediaPeriod mediaPeriod) {
|
public void releasePeriod(MediaPeriod mediaPeriod) {
|
||||||
MediaSourceHolder holder =
|
MediaSourceHolder holder = checkNotNull(mediaSourceByMediaPeriod.remove(mediaPeriod));
|
||||||
Assertions.checkNotNull(mediaSourceByMediaPeriod.remove(mediaPeriod));
|
|
||||||
holder.mediaSource.releasePeriod(mediaPeriod);
|
holder.mediaSource.releasePeriod(mediaPeriod);
|
||||||
holder.activeMediaPeriodIds.remove(((MaskingMediaPeriod) mediaPeriod).id);
|
holder.activeMediaPeriodIds.remove(((MaskingMediaPeriod) mediaPeriod).id);
|
||||||
if (!mediaSourceByMediaPeriod.isEmpty()) {
|
if (!mediaSourceByMediaPeriod.isEmpty()) {
|
||||||
@ -450,8 +450,7 @@ import java.util.Set;
|
|||||||
private void maybeReleaseChildSource(MediaSourceHolder mediaSourceHolder) {
|
private void maybeReleaseChildSource(MediaSourceHolder mediaSourceHolder) {
|
||||||
// Release if the source has been removed from the playlist and no periods are still active.
|
// Release if the source has been removed from the playlist and no periods are still active.
|
||||||
if (mediaSourceHolder.isRemoved && mediaSourceHolder.activeMediaPeriodIds.isEmpty()) {
|
if (mediaSourceHolder.isRemoved && mediaSourceHolder.activeMediaPeriodIds.isEmpty()) {
|
||||||
MediaSourceAndListener removedChild =
|
MediaSourceAndListener removedChild = checkNotNull(childSources.remove(mediaSourceHolder));
|
||||||
Assertions.checkNotNull(childSources.remove(mediaSourceHolder));
|
|
||||||
removedChild.mediaSource.releaseSource(removedChild.caller);
|
removedChild.mediaSource.releaseSource(removedChild.caller);
|
||||||
removedChild.mediaSource.removeEventListener(removedChild.eventListener);
|
removedChild.mediaSource.removeEventListener(removedChild.eventListener);
|
||||||
removedChild.mediaSource.removeDrmEventListener(removedChild.eventListener);
|
removedChild.mediaSource.removeDrmEventListener(removedChild.eventListener);
|
||||||
@ -526,12 +525,8 @@ import java.util.Set;
|
|||||||
implements MediaSourceEventListener, DrmSessionEventListener {
|
implements MediaSourceEventListener, DrmSessionEventListener {
|
||||||
|
|
||||||
private final MediaSourceList.MediaSourceHolder id;
|
private final MediaSourceList.MediaSourceHolder id;
|
||||||
private MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher;
|
|
||||||
private DrmSessionEventListener.EventDispatcher drmEventDispatcher;
|
|
||||||
|
|
||||||
public ForwardingEventListener(MediaSourceList.MediaSourceHolder id) {
|
public ForwardingEventListener(MediaSourceList.MediaSourceHolder id) {
|
||||||
mediaSourceEventDispatcher = MediaSourceList.this.mediaSourceEventDispatcher;
|
|
||||||
drmEventDispatcher = MediaSourceList.this.drmEventDispatcher;
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,8 +538,14 @@ import java.util.Set;
|
|||||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
||||||
LoadEventInfo loadEventData,
|
LoadEventInfo loadEventData,
|
||||||
MediaLoadData mediaLoadData) {
|
MediaLoadData mediaLoadData) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
mediaSourceEventDispatcher.loadStarted(loadEventData, mediaLoadData);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onLoadStarted(
|
||||||
|
eventParameters.first, eventParameters.second, loadEventData, mediaLoadData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,8 +555,14 @@ import java.util.Set;
|
|||||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
||||||
LoadEventInfo loadEventData,
|
LoadEventInfo loadEventData,
|
||||||
MediaLoadData mediaLoadData) {
|
MediaLoadData mediaLoadData) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
mediaSourceEventDispatcher.loadCompleted(loadEventData, mediaLoadData);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onLoadCompleted(
|
||||||
|
eventParameters.first, eventParameters.second, loadEventData, mediaLoadData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,8 +572,14 @@ import java.util.Set;
|
|||||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
||||||
LoadEventInfo loadEventData,
|
LoadEventInfo loadEventData,
|
||||||
MediaLoadData mediaLoadData) {
|
MediaLoadData mediaLoadData) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
mediaSourceEventDispatcher.loadCanceled(loadEventData, mediaLoadData);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onLoadCanceled(
|
||||||
|
eventParameters.first, eventParameters.second, loadEventData, mediaLoadData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,8 +591,19 @@ import java.util.Set;
|
|||||||
MediaLoadData mediaLoadData,
|
MediaLoadData mediaLoadData,
|
||||||
IOException error,
|
IOException error,
|
||||||
boolean wasCanceled) {
|
boolean wasCanceled) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
mediaSourceEventDispatcher.loadError(loadEventData, mediaLoadData, error, wasCanceled);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onLoadError(
|
||||||
|
eventParameters.first,
|
||||||
|
eventParameters.second,
|
||||||
|
loadEventData,
|
||||||
|
mediaLoadData,
|
||||||
|
error,
|
||||||
|
wasCanceled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,8 +612,14 @@ import java.util.Set;
|
|||||||
int windowIndex,
|
int windowIndex,
|
||||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
||||||
MediaLoadData mediaLoadData) {
|
MediaLoadData mediaLoadData) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
mediaSourceEventDispatcher.upstreamDiscarded(mediaLoadData);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onUpstreamDiscarded(
|
||||||
|
eventParameters.first, checkNotNull(eventParameters.second), mediaLoadData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,8 +628,14 @@ import java.util.Set;
|
|||||||
int windowIndex,
|
int windowIndex,
|
||||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
||||||
MediaLoadData mediaLoadData) {
|
MediaLoadData mediaLoadData) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
mediaSourceEventDispatcher.downstreamFormatChanged(mediaLoadData);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onDownstreamFormatChanged(
|
||||||
|
eventParameters.first, eventParameters.second, mediaLoadData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,75 +646,94 @@ import java.util.Set;
|
|||||||
int windowIndex,
|
int windowIndex,
|
||||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
@Nullable MediaSource.MediaPeriodId mediaPeriodId,
|
||||||
@DrmSession.State int state) {
|
@DrmSession.State int state) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
drmEventDispatcher.drmSessionAcquired(state);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onDrmSessionAcquired(
|
||||||
|
eventParameters.first, eventParameters.second, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmKeysLoaded(
|
public void onDrmKeysLoaded(
|
||||||
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
drmEventDispatcher.drmKeysLoaded();
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() -> eventListener.onDrmKeysLoaded(eventParameters.first, eventParameters.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmSessionManagerError(
|
public void onDrmSessionManagerError(
|
||||||
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, Exception error) {
|
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, Exception error) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
drmEventDispatcher.drmSessionManagerError(error);
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onDrmSessionManagerError(
|
||||||
|
eventParameters.first, eventParameters.second, error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmKeysRestored(
|
public void onDrmKeysRestored(
|
||||||
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
drmEventDispatcher.drmKeysRestored();
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() -> eventListener.onDrmKeysRestored(eventParameters.first, eventParameters.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmKeysRemoved(
|
public void onDrmKeysRemoved(
|
||||||
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
drmEventDispatcher.drmKeysRemoved();
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() -> eventListener.onDrmKeysRemoved(eventParameters.first, eventParameters.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmSessionReleased(
|
public void onDrmSessionReleased(
|
||||||
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
|
||||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
@Nullable
|
||||||
drmEventDispatcher.drmSessionReleased();
|
Pair<Integer, MediaSource.@NullableType MediaPeriodId> eventParameters =
|
||||||
|
getEventParameters(windowIndex, mediaPeriodId);
|
||||||
|
if (eventParameters != null) {
|
||||||
|
eventHandler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onDrmSessionReleased(eventParameters.first, eventParameters.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates the event dispatcher and returns whether the event should be dispatched. */
|
/** Updates the event parameters and returns whether the event should be dispatched. */
|
||||||
private boolean maybeUpdateEventDispatcher(
|
@Nullable
|
||||||
|
private Pair<Integer, MediaSource.@NullableType MediaPeriodId> getEventParameters(
|
||||||
int childWindowIndex, @Nullable MediaSource.MediaPeriodId childMediaPeriodId) {
|
int childWindowIndex, @Nullable MediaSource.MediaPeriodId childMediaPeriodId) {
|
||||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId = null;
|
@Nullable MediaSource.MediaPeriodId mediaPeriodId = null;
|
||||||
if (childMediaPeriodId != null) {
|
if (childMediaPeriodId != null) {
|
||||||
mediaPeriodId = getMediaPeriodIdForChildMediaPeriodId(id, childMediaPeriodId);
|
mediaPeriodId = getMediaPeriodIdForChildMediaPeriodId(id, childMediaPeriodId);
|
||||||
if (mediaPeriodId == null) {
|
if (mediaPeriodId == null) {
|
||||||
// Media period not found. Ignore event.
|
// Media period not found. Ignore event.
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int windowIndex = getWindowIndexForChildWindowIndex(id, childWindowIndex);
|
int windowIndex = getWindowIndexForChildWindowIndex(id, childWindowIndex);
|
||||||
if (mediaSourceEventDispatcher.windowIndex != windowIndex
|
return Pair.create(windowIndex, mediaPeriodId);
|
||||||
|| !Util.areEqual(mediaSourceEventDispatcher.mediaPeriodId, mediaPeriodId)) {
|
|
||||||
mediaSourceEventDispatcher =
|
|
||||||
MediaSourceList.this.mediaSourceEventDispatcher.withParameters(
|
|
||||||
windowIndex, mediaPeriodId, /* mediaTimeOffsetMs= */ 0L);
|
|
||||||
}
|
|
||||||
if (drmEventDispatcher.windowIndex != windowIndex
|
|
||||||
|| !Util.areEqual(drmEventDispatcher.mediaPeriodId, mediaPeriodId)) {
|
|
||||||
drmEventDispatcher =
|
|
||||||
MediaSourceList.this.drmEventDispatcher.withParameters(windowIndex, mediaPeriodId);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,7 @@ import androidx.media3.common.TrackGroup;
|
|||||||
import androidx.media3.common.Tracks;
|
import androidx.media3.common.Tracks;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
|
import androidx.media3.common.util.SystemClock;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||||
@ -11897,7 +11898,11 @@ public final class ExoPlayerTest {
|
|||||||
new TestExoPlayerBuilder(context)
|
new TestExoPlayerBuilder(context)
|
||||||
.setRenderersFactory(
|
.setRenderersFactory(
|
||||||
(handler, videoListener, audioListener, textOutput, metadataOutput) -> {
|
(handler, videoListener, audioListener, textOutput, metadataOutput) -> {
|
||||||
videoRenderer.set(new FakeVideoRenderer(handler, videoListener));
|
videoRenderer.set(
|
||||||
|
new FakeVideoRenderer(
|
||||||
|
SystemClock.DEFAULT.createHandler(
|
||||||
|
handler.getLooper(), /* callback= */ null),
|
||||||
|
videoListener));
|
||||||
return new Renderer[] {videoRenderer.get()};
|
return new Renderer[] {videoRenderer.get()};
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
@ -12034,7 +12039,12 @@ public final class ExoPlayerTest {
|
|||||||
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
||||||
.setRenderersFactory(
|
.setRenderersFactory(
|
||||||
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
|
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
|
||||||
new Renderer[] {new FakeVideoRenderer(handler, videoListener)})
|
new Renderer[] {
|
||||||
|
new FakeVideoRenderer(
|
||||||
|
SystemClock.DEFAULT.createHandler(
|
||||||
|
handler.getLooper(), /* callback= */ null),
|
||||||
|
videoListener)
|
||||||
|
})
|
||||||
.build();
|
.build();
|
||||||
AnalyticsListener listener = mock(AnalyticsListener.class);
|
AnalyticsListener listener = mock(AnalyticsListener.class);
|
||||||
player.addAnalyticsListener(listener);
|
player.addAnalyticsListener(listener);
|
||||||
@ -12059,7 +12069,12 @@ public final class ExoPlayerTest {
|
|||||||
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
||||||
.setRenderersFactory(
|
.setRenderersFactory(
|
||||||
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
|
(handler, videoListener, audioListener, textOutput, metadataOutput) ->
|
||||||
new Renderer[] {new FakeVideoRenderer(handler, videoListener)})
|
new Renderer[] {
|
||||||
|
new FakeVideoRenderer(
|
||||||
|
SystemClock.DEFAULT.createHandler(
|
||||||
|
handler.getLooper(), /* callback= */ null),
|
||||||
|
videoListener)
|
||||||
|
})
|
||||||
.build();
|
.build();
|
||||||
Player.Listener listener = mock(Player.Listener.class);
|
Player.Listener listener = mock(Player.Listener.class);
|
||||||
player.addListener(listener);
|
player.addListener(listener);
|
||||||
|
@ -25,7 +25,6 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.robolectric.Shadows.shadowOf;
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import androidx.media3.common.AdPlaybackState;
|
import androidx.media3.common.AdPlaybackState;
|
||||||
@ -36,6 +35,7 @@ import androidx.media3.common.Player;
|
|||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.Tracks;
|
import androidx.media3.common.Tracks;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||||
import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
|
import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
|
||||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
@ -97,13 +97,14 @@ public final class MediaPeriodQueueTest {
|
|||||||
analyticsCollector.setPlayer(
|
analyticsCollector.setPlayer(
|
||||||
new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(),
|
new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(),
|
||||||
Looper.getMainLooper());
|
Looper.getMainLooper());
|
||||||
mediaPeriodQueue =
|
HandlerWrapper handler =
|
||||||
new MediaPeriodQueue(analyticsCollector, new Handler(Looper.getMainLooper()));
|
Clock.DEFAULT.createHandler(Looper.getMainLooper(), /* callback= */ null);
|
||||||
|
mediaPeriodQueue = new MediaPeriodQueue(analyticsCollector, handler);
|
||||||
mediaSourceList =
|
mediaSourceList =
|
||||||
new MediaSourceList(
|
new MediaSourceList(
|
||||||
mock(MediaSourceList.MediaSourceListInfoRefreshListener.class),
|
mock(MediaSourceList.MediaSourceListInfoRefreshListener.class),
|
||||||
analyticsCollector,
|
analyticsCollector,
|
||||||
new Handler(Looper.getMainLooper()),
|
handler,
|
||||||
PlayerId.UNSET);
|
PlayerId.UNSET);
|
||||||
rendererCapabilities = new RendererCapabilities[0];
|
rendererCapabilities = new RendererCapabilities[0];
|
||||||
trackSelector = mock(TrackSelector.class);
|
trackSelector = mock(TrackSelector.class);
|
||||||
|
@ -67,7 +67,7 @@ public class MediaSourceListTest {
|
|||||||
new MediaSourceList(
|
new MediaSourceList(
|
||||||
mock(MediaSourceList.MediaSourceListInfoRefreshListener.class),
|
mock(MediaSourceList.MediaSourceListInfoRefreshListener.class),
|
||||||
analyticsCollector,
|
analyticsCollector,
|
||||||
Util.createHandlerForCurrentOrMainLooper(),
|
Clock.DEFAULT.createHandler(Util.getCurrentOrMainLooper(), /* callback= */ null),
|
||||||
PlayerId.UNSET);
|
PlayerId.UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,12 @@ import static androidx.media3.exoplayer.analytics.AnalyticsListener.EVENT_VIDEO_
|
|||||||
import static androidx.media3.exoplayer.analytics.AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED;
|
import static androidx.media3.exoplayer.analytics.AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED;
|
||||||
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
|
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
|
||||||
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
||||||
|
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.playUntilPosition;
|
||||||
|
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilError;
|
||||||
|
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilIsLoading;
|
||||||
|
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled;
|
||||||
|
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilPlaybackState;
|
||||||
|
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilTimelineChanged;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
@ -63,6 +69,8 @@ import static org.mockito.Mockito.inOrder;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.robolectric.shadows.ShadowLooper.idleMainLooper;
|
||||||
|
import static org.robolectric.shadows.ShadowLooper.runMainLooperToNextTask;
|
||||||
|
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@ -85,6 +93,7 @@ import androidx.media3.common.Tracks;
|
|||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.exoplayer.DecoderCounters;
|
import androidx.media3.exoplayer.DecoderCounters;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
@ -102,8 +111,6 @@ import androidx.media3.exoplayer.source.LoadEventInfo;
|
|||||||
import androidx.media3.exoplayer.source.MediaLoadData;
|
import androidx.media3.exoplayer.source.MediaLoadData;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.test.utils.ActionSchedule;
|
|
||||||
import androidx.media3.test.utils.ActionSchedule.PlayerRunnable;
|
|
||||||
import androidx.media3.test.utils.ExoPlayerTestRunner;
|
import androidx.media3.test.utils.ExoPlayerTestRunner;
|
||||||
import androidx.media3.test.utils.FakeAudioRenderer;
|
import androidx.media3.test.utils.FakeAudioRenderer;
|
||||||
import androidx.media3.test.utils.FakeClock;
|
import androidx.media3.test.utils.FakeClock;
|
||||||
@ -132,14 +139,11 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import org.robolectric.shadows.ShadowLooper;
|
|
||||||
|
|
||||||
/** Integration test for {@link DefaultAnalyticsCollector}. */
|
/** Integration test for {@link DefaultAnalyticsCollector}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class DefaultAnalyticsCollectorTest {
|
public final class DefaultAnalyticsCollectorTest {
|
||||||
|
|
||||||
private static final String TAG = "DefaultAnalyticsCollectorTest";
|
|
||||||
|
|
||||||
// Deprecated event constants.
|
// Deprecated event constants.
|
||||||
private static final long EVENT_PLAYER_STATE_CHANGED = 1L << 63;
|
private static final long EVENT_PLAYER_STATE_CHANGED = 1L << 63;
|
||||||
private static final long EVENT_SEEK_STARTED = 1L << 62;
|
private static final long EVENT_SEEK_STARTED = 1L << 62;
|
||||||
@ -167,7 +171,6 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
private static final Format VIDEO_FORMAT_DRM_1 =
|
private static final Format VIDEO_FORMAT_DRM_1 =
|
||||||
ExoPlayerTestRunner.VIDEO_FORMAT.buildUpon().setDrmInitData(DRM_DATA_1).build();
|
ExoPlayerTestRunner.VIDEO_FORMAT.buildUpon().setDrmInitData(DRM_DATA_1).build();
|
||||||
|
|
||||||
private static final int TIMEOUT_MS = 10_000;
|
|
||||||
private static final Timeline SINGLE_PERIOD_TIMELINE = new FakeTimeline();
|
private static final Timeline SINGLE_PERIOD_TIMELINE = new FakeTimeline();
|
||||||
private static final EventWindowAndPeriodId WINDOW_0 =
|
private static final EventWindowAndPeriodId WINDOW_0 =
|
||||||
new EventWindowAndPeriodId(/* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
new EventWindowAndPeriodId(/* windowIndex= */ 0, /* mediaPeriodId= */ null);
|
||||||
@ -217,7 +220,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
FakeMediaSource mediaSource =
|
FakeMediaSource mediaSource =
|
||||||
new FakeMediaSource(
|
new FakeMediaSource(
|
||||||
Timeline.EMPTY, ExoPlayerTestRunner.VIDEO_FORMAT, ExoPlayerTestRunner.AUDIO_FORMAT);
|
Timeline.EMPTY, ExoPlayerTestRunner.VIDEO_FORMAT, ExoPlayerTestRunner.AUDIO_FORMAT);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
ExoPlayer player = setupPlayer();
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
|
player.play();
|
||||||
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
@ -236,7 +246,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
SINGLE_PERIOD_TIMELINE,
|
SINGLE_PERIOD_TIMELINE,
|
||||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||||
ExoPlayerTestRunner.AUDIO_FORMAT);
|
ExoPlayerTestRunner.AUDIO_FORMAT);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
ExoPlayer player = setupPlayer();
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
|
player.play();
|
||||||
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
@ -247,7 +264,7 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
period0 /* ENDED */)
|
period0 /* ENDED */)
|
||||||
.inOrder();
|
.inOrder();
|
||||||
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
||||||
.containsExactly(WINDOW_0 /* PLAYLIST_CHANGED */, period0 /* SOURCE_UPDATE */)
|
.containsExactly(WINDOW_0 /* PLAYLIST_CHANGED */, WINDOW_0 /* SOURCE_UPDATE */)
|
||||||
.inOrder();
|
.inOrder();
|
||||||
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
|
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
|
||||||
.containsExactly(period0 /* started */, period0 /* stopped */)
|
.containsExactly(period0 /* started */, period0 /* stopped */)
|
||||||
@ -297,7 +314,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
SINGLE_PERIOD_TIMELINE,
|
SINGLE_PERIOD_TIMELINE,
|
||||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||||
ExoPlayerTestRunner.AUDIO_FORMAT));
|
ExoPlayerTestRunner.AUDIO_FORMAT));
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
ExoPlayer player = setupPlayer();
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
|
player.play();
|
||||||
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
@ -378,7 +402,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
new ConcatenatingMediaSource(
|
new ConcatenatingMediaSource(
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT),
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT),
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.AUDIO_FORMAT));
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.AUDIO_FORMAT));
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
ExoPlayer player = setupPlayer();
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
|
player.play();
|
||||||
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
@ -449,23 +480,23 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||||
ExoPlayerTestRunner.AUDIO_FORMAT),
|
ExoPlayerTestRunner.AUDIO_FORMAT),
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.AUDIO_FORMAT));
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.AUDIO_FORMAT));
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
// Wait until second period has fully loaded to assert loading events without flakiness.
|
|
||||||
.waitForIsLoading(true)
|
player.setMediaSource(mediaSource);
|
||||||
.waitForIsLoading(false)
|
player.prepare();
|
||||||
.seek(/* mediaItemIndex= */ 1, /* positionMs= */ 0)
|
// Wait until second period has fully loaded to assert loading events.
|
||||||
.play()
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
.build();
|
runUntilIsLoading(player, /* expectedIsLoading= */ false);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
|
player.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 0);
|
||||||
|
player.play();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
period0 /* READY */,
|
period0 /* READY */,
|
||||||
period1 /* BUFFERING */,
|
period1 /* BUFFERING */,
|
||||||
period1 /* setPlayWhenReady=true */,
|
period1 /* setPlayWhenReady=true */,
|
||||||
@ -542,23 +573,24 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
SINGLE_PERIOD_TIMELINE,
|
SINGLE_PERIOD_TIMELINE,
|
||||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||||
ExoPlayerTestRunner.AUDIO_FORMAT));
|
ExoPlayerTestRunner.AUDIO_FORMAT));
|
||||||
long periodDurationMs =
|
long windowDurationMs =
|
||||||
SINGLE_PERIOD_TIMELINE.getWindow(/* windowIndex= */ 0, new Window()).getDurationMs();
|
SINGLE_PERIOD_TIMELINE.getWindow(/* windowIndex= */ 0, new Window()).getDurationMs();
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
|
||||||
.playUntilPosition(/* mediaItemIndex= */ 0, periodDurationMs)
|
player.setMediaSource(mediaSource);
|
||||||
.seekAndWait(/* positionMs= */ 0)
|
player.prepare();
|
||||||
.play()
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
.build();
|
playUntilPosition(player, /* mediaItemIndex= */ 0, windowDurationMs - 100);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
|
player.seekTo(/* positionMs= */ 0);
|
||||||
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
|
player.play();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
period0 /* READY */,
|
period0 /* READY */,
|
||||||
period0 /* setPlayWhenReady=true */,
|
period0 /* setPlayWhenReady=true */,
|
||||||
@ -653,17 +685,19 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
||||||
MediaSource mediaSource2 =
|
MediaSource mediaSource2 =
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
|
||||||
.setMediaSources(/* resetPosition= */ false, mediaSource2)
|
player.setMediaSource(mediaSource1);
|
||||||
.waitForTimelineChanged()
|
player.prepare();
|
||||||
// Wait until loading started to prevent flakiness caused by loading finishing too fast.
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
.waitForIsLoading(true)
|
player.setMediaSource(mediaSource2, /* resetPosition= */ false);
|
||||||
.play()
|
runUntilTimelineChanged(player);
|
||||||
.build();
|
// Wait until loading started to assert loading events.
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource1, actionSchedule);
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
|
player.play();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
// Populate all event ids with last timeline (after second prepare).
|
// Populate all event ids with last timeline (after second prepare).
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
@ -676,9 +710,7 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
/* windowSequenceNumber= */ 0));
|
/* windowSequenceNumber= */ 0));
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
period0Seq0 /* READY */,
|
period0Seq0 /* READY */,
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
period0Seq1 /* setPlayWhenReady=true */,
|
period0Seq1 /* setPlayWhenReady=true */,
|
||||||
@ -688,9 +720,9 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* PLAYLIST_CHANGE */,
|
WINDOW_0 /* PLAYLIST_CHANGE */,
|
||||||
period0Seq0 /* SOURCE_UPDATE */,
|
WINDOW_0 /* SOURCE_UPDATE */,
|
||||||
WINDOW_0 /* PLAYLIST_CHANGE */,
|
WINDOW_0 /* PLAYLIST_CHANGE */,
|
||||||
period0Seq1 /* SOURCE_UPDATE */);
|
WINDOW_0 /* SOURCE_UPDATE */);
|
||||||
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY))
|
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY))
|
||||||
.containsExactly(WINDOW_0 /* REMOVE */);
|
.containsExactly(WINDOW_0 /* REMOVE */);
|
||||||
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
|
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
|
||||||
@ -753,28 +785,31 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
public void reprepareAfterError() throws Exception {
|
public void reprepareAfterError() throws Exception {
|
||||||
MediaSource mediaSource =
|
MediaSource mediaSource =
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
|
||||||
.throwPlaybackException(
|
player.setMediaSource(mediaSource);
|
||||||
ExoPlaybackException.createForSource(
|
player.prepare();
|
||||||
new IOException(), PlaybackException.ERROR_CODE_IO_UNSPECIFIED))
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
.waitForPlaybackState(Player.STATE_IDLE)
|
player
|
||||||
.seek(/* positionMs= */ 0)
|
.createMessage(
|
||||||
.prepare()
|
(message, payload) -> {
|
||||||
// Wait until loading started to assert loading events without flakiness.
|
throw ExoPlaybackException.createForSource(
|
||||||
.waitForIsLoading(true)
|
new IOException(), PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
|
||||||
.play()
|
})
|
||||||
.waitForPlaybackState(Player.STATE_ENDED)
|
.send();
|
||||||
.build();
|
runUntilError(player);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
|
player.seekTo(/* positionMs= */ 0);
|
||||||
|
player.prepare();
|
||||||
|
// Wait until loading started to assert loading events.
|
||||||
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
|
player.play();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
period0Seq0 /* READY */,
|
period0Seq0 /* READY */,
|
||||||
period0Seq0 /* IDLE */,
|
period0Seq0 /* IDLE */,
|
||||||
@ -784,7 +819,7 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
period0Seq0 /* ENDED */)
|
period0Seq0 /* ENDED */)
|
||||||
.inOrder();
|
.inOrder();
|
||||||
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
||||||
.containsExactly(WINDOW_0 /* prepared */, period0Seq0 /* prepared */);
|
.containsExactly(WINDOW_0 /* prepared */, WINDOW_0 /* prepared */);
|
||||||
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period0Seq0);
|
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period0Seq0);
|
||||||
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0Seq0);
|
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0Seq0);
|
||||||
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period0Seq0);
|
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period0Seq0);
|
||||||
@ -835,36 +870,33 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
||||||
final ConcatenatingMediaSource concatenatedMediaSource =
|
final ConcatenatingMediaSource concatenatedMediaSource =
|
||||||
new ConcatenatingMediaSource(childMediaSource, childMediaSource);
|
new ConcatenatingMediaSource(childMediaSource, childMediaSource);
|
||||||
long periodDurationMs =
|
long windowDurationMs =
|
||||||
SINGLE_PERIOD_TIMELINE.getWindow(/* windowIndex= */ 0, new Window()).getDurationMs();
|
SINGLE_PERIOD_TIMELINE.getWindow(/* windowIndex= */ 0, new Window()).getDurationMs();
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
|
||||||
// Ensure second period is already being read from.
|
player.setMediaSource(concatenatedMediaSource);
|
||||||
.playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ periodDurationMs)
|
player.prepare();
|
||||||
.executeRunnable(
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
() ->
|
// Ensure second period is already being read from.
|
||||||
concatenatedMediaSource.moveMediaSource(
|
playUntilPosition(player, /* mediaItemIndex= */ 0, /* positionMs= */ windowDurationMs - 100);
|
||||||
/* currentIndex= */ 0, /* newIndex= */ 1))
|
concatenatedMediaSource.moveMediaSource(/* currentIndex= */ 0, /* newIndex= */ 1);
|
||||||
.waitForTimelineChanged()
|
runUntilTimelineChanged(player);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
.play()
|
player.play();
|
||||||
.build();
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(concatenatedMediaSource, actionSchedule);
|
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
window0Period1Seq0 /* READY */,
|
window0Period1Seq0 /* READY */,
|
||||||
window0Period1Seq0 /* setPlayWhenReady=true */,
|
window0Period1Seq0 /* setPlayWhenReady=true */,
|
||||||
window0Period1Seq0 /* setPlayWhenReady=false */,
|
window0Period1Seq0 /* setPlayWhenReady=false */,
|
||||||
period1Seq0 /* setPlayWhenReady=true */,
|
|
||||||
period1Seq0 /* BUFFERING */,
|
period1Seq0 /* BUFFERING */,
|
||||||
period1Seq0 /* READY */,
|
period1Seq0 /* READY */,
|
||||||
|
period1Seq0 /* setPlayWhenReady=true */,
|
||||||
period1Seq0 /* ENDED */)
|
period1Seq0 /* ENDED */)
|
||||||
.inOrder();
|
.inOrder();
|
||||||
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
||||||
@ -926,20 +958,22 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
public void playlistOperations() throws Exception {
|
public void playlistOperations() throws Exception {
|
||||||
MediaSource fakeMediaSource =
|
MediaSource fakeMediaSource =
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, ExoPlayerTestRunner.VIDEO_FORMAT);
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
|
||||||
.addMediaSources(fakeMediaSource)
|
player.setMediaSource(fakeMediaSource);
|
||||||
// Wait until second period has fully loaded to assert loading events without flakiness.
|
player.prepare();
|
||||||
.waitForIsLoading(true)
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
.waitForIsLoading(false)
|
player.addMediaSource(fakeMediaSource);
|
||||||
.removeMediaItem(/* index= */ 0)
|
// Wait until second period has fully loaded to assert loading events.
|
||||||
.waitForPlaybackState(Player.STATE_BUFFERING)
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
runUntilIsLoading(player, /* expectedIsLoading= */ false);
|
||||||
.play()
|
player.removeMediaItem(/* index= */ 0);
|
||||||
.build();
|
runUntilPlaybackState(player, Player.STATE_BUFFERING);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(fakeMediaSource, actionSchedule);
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
|
player.play();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
// Populate event ids with second to last timeline that still contained both periods.
|
// Populate event ids with second to last timeline that still contained both periods.
|
||||||
populateEventIds(listener.reportedTimelines.get(listener.reportedTimelines.size() - 2));
|
populateEventIds(listener.reportedTimelines.get(listener.reportedTimelines.size() - 2));
|
||||||
@ -953,8 +987,6 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
/* windowSequenceNumber= */ 1));
|
/* windowSequenceNumber= */ 1));
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
period0Seq0 /* READY */,
|
period0Seq0 /* READY */,
|
||||||
period0Seq1 /* BUFFERING */,
|
period0Seq1 /* BUFFERING */,
|
||||||
@ -965,7 +997,7 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* PLAYLIST_CHANGED */,
|
WINDOW_0 /* PLAYLIST_CHANGED */,
|
||||||
period0Seq0 /* SOURCE_UPDATE (first item) */,
|
WINDOW_0 /* SOURCE_UPDATE (first item) */,
|
||||||
period0Seq0 /* PLAYLIST_CHANGED (add) */,
|
period0Seq0 /* PLAYLIST_CHANGED (add) */,
|
||||||
period0Seq0 /* SOURCE_UPDATE (second item) */,
|
period0Seq0 /* SOURCE_UPDATE (second item) */,
|
||||||
period0Seq1 /* PLAYLIST_CHANGED (remove) */)
|
period0Seq1 /* PLAYLIST_CHANGED (remove) */)
|
||||||
@ -1063,60 +1095,53 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExoPlayerTestRunner.VIDEO_FORMAT);
|
ExoPlayerTestRunner.VIDEO_FORMAT);
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
player.addListener(
|
||||||
.executeRunnable(
|
new Player.Listener() {
|
||||||
new PlayerRunnable() {
|
@Override
|
||||||
@Override
|
public void onPositionDiscontinuity(
|
||||||
public void run(ExoPlayer player) {
|
Player.PositionInfo oldPosition,
|
||||||
player.addListener(
|
Player.PositionInfo newPosition,
|
||||||
new Player.Listener() {
|
@Player.DiscontinuityReason int reason) {
|
||||||
@Override
|
if (!player.isPlayingAd() && reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||||
public void onPositionDiscontinuity(
|
// Finished playing ad. Marked as played.
|
||||||
Player.PositionInfo oldPosition,
|
adPlaybackState.set(
|
||||||
Player.PositionInfo newPosition,
|
adPlaybackState
|
||||||
@Player.DiscontinuityReason int reason) {
|
.get()
|
||||||
if (!player.isPlayingAd()
|
.withPlayedAd(
|
||||||
&& reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
/* adGroupIndex= */ playedAdCount.getAndIncrement(),
|
||||||
// Finished playing ad. Marked as played.
|
/* adIndexInAdGroup= */ 0));
|
||||||
adPlaybackState.set(
|
fakeMediaSource.setNewSourceInfo(
|
||||||
adPlaybackState
|
new FakeTimeline(
|
||||||
.get()
|
new TimelineWindowDefinition(
|
||||||
.withPlayedAd(
|
/* periodCount= */ 1,
|
||||||
/* adGroupIndex= */ playedAdCount.getAndIncrement(),
|
/* id= */ 0,
|
||||||
/* adIndexInAdGroup= */ 0));
|
/* isSeekable= */ true,
|
||||||
fakeMediaSource.setNewSourceInfo(
|
/* isDynamic= */ false,
|
||||||
new FakeTimeline(
|
contentDurationsUs,
|
||||||
new TimelineWindowDefinition(
|
adPlaybackState.get())),
|
||||||
/* periodCount= */ 1,
|
/* sendManifestLoadEvents= */ false);
|
||||||
/* id= */ 0,
|
}
|
||||||
/* isSeekable= */ true,
|
}
|
||||||
/* isDynamic= */ false,
|
});
|
||||||
contentDurationsUs,
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
adPlaybackState.get())),
|
player.addAnalyticsListener(listener);
|
||||||
/* sendManifestLoadEvents= */ false);
|
|
||||||
}
|
player.setMediaSource(fakeMediaSource);
|
||||||
}
|
player.prepare();
|
||||||
});
|
// Ensure everything is preloaded.
|
||||||
}
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
})
|
runUntilIsLoading(player, /* expectedIsLoading= */ false);
|
||||||
.pause()
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
// Ensure everything is preloaded.
|
// Wait in each content part to ensure previously triggered events get a chance to be delivered.
|
||||||
.waitForIsLoading(true)
|
playUntilPosition(player, /* mediaItemIndex= */ 0, /* positionMs= */ 3_000);
|
||||||
.waitForIsLoading(false)
|
runUntilPendingCommandsAreFullyHandled(player);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
playUntilPosition(player, /* mediaItemIndex= */ 0, /* positionMs= */ 8_000);
|
||||||
// Wait in each content part to ensure previously triggered events get a chance to be
|
runUntilPendingCommandsAreFullyHandled(player);
|
||||||
// delivered. This prevents flakiness caused by playback progressing too fast.
|
player.play();
|
||||||
.playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 3_000)
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
.waitForPendingPlayerCommands()
|
// Wait for final timeline change that marks post-roll played.
|
||||||
.playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 8_000)
|
runUntilTimelineChanged(player);
|
||||||
.waitForPendingPlayerCommands()
|
|
||||||
.play()
|
|
||||||
.waitForPlaybackState(Player.STATE_ENDED)
|
|
||||||
// Wait for final timeline change that marks post-roll played.
|
|
||||||
.waitForTimelineChanged()
|
|
||||||
.build();
|
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(fakeMediaSource, actionSchedule);
|
|
||||||
|
|
||||||
Object periodUid = listener.lastReportedTimeline.getUidOfPeriod(/* periodIndex= */ 0);
|
Object periodUid = listener.lastReportedTimeline.getUidOfPeriod(/* periodIndex= */ 0);
|
||||||
EventWindowAndPeriodId prerollAd =
|
EventWindowAndPeriodId prerollAd =
|
||||||
@ -1158,8 +1183,6 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
periodUid, /* windowSequenceNumber= */ 0, /* nextAdGroupIndex= */ C.INDEX_UNSET));
|
periodUid, /* windowSequenceNumber= */ 0, /* nextAdGroupIndex= */ C.INDEX_UNSET));
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
prerollAd /* READY */,
|
prerollAd /* READY */,
|
||||||
prerollAd /* setPlayWhenReady=true */,
|
prerollAd /* setPlayWhenReady=true */,
|
||||||
@ -1172,7 +1195,7 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* PLAYLIST_CHANGED */,
|
WINDOW_0 /* PLAYLIST_CHANGED */,
|
||||||
prerollAd /* SOURCE_UPDATE (initial) */,
|
WINDOW_0 /* SOURCE_UPDATE (initial) */,
|
||||||
contentAfterPreroll /* SOURCE_UPDATE (played preroll) */,
|
contentAfterPreroll /* SOURCE_UPDATE (played preroll) */,
|
||||||
contentAfterMidroll /* SOURCE_UPDATE (played midroll) */,
|
contentAfterMidroll /* SOURCE_UPDATE (played midroll) */,
|
||||||
contentAfterPostroll /* SOURCE_UPDATE (played postroll) */)
|
contentAfterPostroll /* SOURCE_UPDATE (played postroll) */)
|
||||||
@ -1322,20 +1345,21 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExoPlayerTestRunner.VIDEO_FORMAT);
|
ExoPlayerTestRunner.VIDEO_FORMAT);
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
// Ensure everything is preloaded.
|
|
||||||
.waitForIsLoading(true)
|
player.setMediaSource(fakeMediaSource);
|
||||||
.waitForIsLoading(false)
|
player.prepare();
|
||||||
// Seek behind the midroll.
|
// Ensure everything is preloaded.
|
||||||
.seek(6 * C.MICROS_PER_SECOND)
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
// Wait until loading started again to assert loading events without flakiness.
|
runUntilIsLoading(player, /* expectedIsLoading= */ false);
|
||||||
.waitForIsLoading(true)
|
// Seek behind the midroll.
|
||||||
.play()
|
player.seekTo(/* positionMs= */ 6_000);
|
||||||
.waitForPlaybackState(Player.STATE_ENDED)
|
// Wait until loading started again to assert loading events.
|
||||||
.build();
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(fakeMediaSource, actionSchedule);
|
player.play();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
Object periodUid = listener.lastReportedTimeline.getUidOfPeriod(/* periodIndex= */ 0);
|
Object periodUid = listener.lastReportedTimeline.getUidOfPeriod(/* periodIndex= */ 0);
|
||||||
EventWindowAndPeriodId midrollAd =
|
EventWindowAndPeriodId midrollAd =
|
||||||
@ -1357,8 +1381,6 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
periodUid, /* windowSequenceNumber= */ 0, /* nextAdGroupIndex= */ C.INDEX_UNSET));
|
periodUid, /* windowSequenceNumber= */ 0, /* nextAdGroupIndex= */ C.INDEX_UNSET));
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
WINDOW_0 /* setPlayWhenReady=true */,
|
|
||||||
WINDOW_0 /* setPlayWhenReady=false */,
|
|
||||||
WINDOW_0 /* BUFFERING */,
|
WINDOW_0 /* BUFFERING */,
|
||||||
contentBeforeMidroll /* READY */,
|
contentBeforeMidroll /* READY */,
|
||||||
contentAfterMidroll /* BUFFERING */,
|
contentAfterMidroll /* BUFFERING */,
|
||||||
@ -1367,7 +1389,7 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
contentAfterMidroll /* ENDED */)
|
contentAfterMidroll /* ENDED */)
|
||||||
.inOrder();
|
.inOrder();
|
||||||
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
|
||||||
.containsExactly(WINDOW_0 /* PLAYLIST_CHANGED */, contentBeforeMidroll /* SOURCE_UPDATE */);
|
.containsExactly(WINDOW_0 /* PLAYLIST_CHANGED */, WINDOW_0 /* SOURCE_UPDATE */);
|
||||||
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY))
|
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY))
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
contentAfterMidroll /* seek */,
|
contentAfterMidroll /* seek */,
|
||||||
@ -1435,21 +1457,17 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
@Test
|
@Test
|
||||||
public void notifyExternalEvents() throws Exception {
|
public void notifyExternalEvents() throws Exception {
|
||||||
MediaSource mediaSource = new FakeMediaSource(SINGLE_PERIOD_TIMELINE);
|
MediaSource mediaSource = new FakeMediaSource(SINGLE_PERIOD_TIMELINE);
|
||||||
ActionSchedule actionSchedule =
|
ExoPlayer player = setupPlayer();
|
||||||
new ActionSchedule.Builder(TAG)
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
.pause()
|
player.addAnalyticsListener(listener);
|
||||||
.waitForPlaybackState(Player.STATE_READY)
|
|
||||||
.executeRunnable(
|
player.setMediaSource(mediaSource);
|
||||||
new PlayerRunnable() {
|
player.prepare();
|
||||||
@Override
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
public void run(ExoPlayer player) {
|
player.getAnalyticsCollector().notifySeekStarted();
|
||||||
player.getAnalyticsCollector().notifySeekStarted();
|
player.seekTo(/* positionMs= */ 0);
|
||||||
}
|
player.play();
|
||||||
})
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
.seek(/* positionMs= */ 0)
|
|
||||||
.play()
|
|
||||||
.build();
|
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
|
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0);
|
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0);
|
||||||
@ -1460,7 +1478,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
public void drmEvents_singlePeriod() throws Exception {
|
public void drmEvents_singlePeriod() throws Exception {
|
||||||
MediaSource mediaSource =
|
MediaSource mediaSource =
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, drmSessionManager, VIDEO_FORMAT_DRM_1);
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, drmSessionManager, VIDEO_FORMAT_DRM_1);
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
ExoPlayer player = setupPlayer();
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
|
player.play();
|
||||||
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).isEmpty();
|
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).isEmpty();
|
||||||
@ -1488,18 +1513,21 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
SINGLE_PERIOD_TIMELINE, blockingDrmSessionManager, VIDEO_FORMAT_DRM_1),
|
SINGLE_PERIOD_TIMELINE, blockingDrmSessionManager, VIDEO_FORMAT_DRM_1),
|
||||||
new FakeMediaSource(
|
new FakeMediaSource(
|
||||||
SINGLE_PERIOD_TIMELINE, blockingDrmSessionManager, VIDEO_FORMAT_DRM_1));
|
SINGLE_PERIOD_TIMELINE, blockingDrmSessionManager, VIDEO_FORMAT_DRM_1));
|
||||||
TestAnalyticsListener listener =
|
ExoPlayer player = setupPlayer();
|
||||||
runAnalyticsTest(
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
mediaSource,
|
player.addAnalyticsListener(listener);
|
||||||
// Wait for the media to be fully buffered before unblocking the DRM key request. This
|
|
||||||
// ensures both periods report the same load event (because period1's DRM session is
|
player.play();
|
||||||
// already preacquired by the time the key load completes).
|
player.setMediaSource(mediaSource);
|
||||||
new ActionSchedule.Builder(TAG)
|
player.prepare();
|
||||||
.waitForIsLoading(false)
|
// Wait for the media to be fully buffered before unblocking the DRM key request. This
|
||||||
.waitForIsLoading(true)
|
// ensures both periods report the same load event (because period1's DRM session is
|
||||||
.waitForIsLoading(false)
|
// already preacquired by the time the key load completes).
|
||||||
.executeRunnable(mediaDrmCallback.keyCondition::open)
|
runUntilIsLoading(player, /* expectedIsLoading= */ false);
|
||||||
.build());
|
runUntilIsLoading(player, /* expectedIsLoading= */ true);
|
||||||
|
runUntilIsLoading(player, /* expectedIsLoading= */ false);
|
||||||
|
mediaDrmCallback.keyCondition.open();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).isEmpty();
|
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).isEmpty();
|
||||||
@ -1525,7 +1553,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
SINGLE_PERIOD_TIMELINE,
|
SINGLE_PERIOD_TIMELINE,
|
||||||
drmSessionManager,
|
drmSessionManager,
|
||||||
VIDEO_FORMAT_DRM_1.buildUpon().setDrmInitData(DRM_DATA_2).build()));
|
VIDEO_FORMAT_DRM_1.buildUpon().setDrmInitData(DRM_DATA_2).build()));
|
||||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
ExoPlayer player = setupPlayer();
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
|
player.play();
|
||||||
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
|
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).isEmpty();
|
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).isEmpty();
|
||||||
@ -1552,13 +1587,16 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
.build(mediaDrmCallback);
|
.build(mediaDrmCallback);
|
||||||
MediaSource mediaSource =
|
MediaSource mediaSource =
|
||||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, failingDrmSessionManager, VIDEO_FORMAT_DRM_1);
|
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, failingDrmSessionManager, VIDEO_FORMAT_DRM_1);
|
||||||
TestAnalyticsListener listener =
|
ExoPlayer player = setupPlayer();
|
||||||
runAnalyticsTest(
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
mediaSource,
|
player.addAnalyticsListener(listener);
|
||||||
new ActionSchedule.Builder(TAG)
|
|
||||||
.waitForIsLoading(false)
|
player.play();
|
||||||
.executeRunnable(mediaDrmCallback.keyCondition::open)
|
player.setMediaSource(mediaSource);
|
||||||
.build());
|
player.prepare();
|
||||||
|
runUntilIsLoading(player, /* expectedIsLoading= */ false);
|
||||||
|
mediaDrmCallback.keyCondition.open();
|
||||||
|
runUntilError(player);
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).containsExactly(period0);
|
assertThat(listener.getEvents(EVENT_DRM_SESSION_MANAGER_ERROR)).containsExactly(period0);
|
||||||
@ -1588,12 +1626,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ExoPlayer player = setupPlayer(renderersFactory);
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
TestAnalyticsListener listener =
|
player.play();
|
||||||
runAnalyticsTest(
|
player.setMediaSource(new ConcatenatingMediaSource(source0, source1));
|
||||||
new ConcatenatingMediaSource(source0, source1),
|
player.prepare();
|
||||||
/* actionSchedule= */ null,
|
runUntilError(player);
|
||||||
renderersFactory);
|
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period1);
|
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period1);
|
||||||
@ -1622,12 +1662,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ExoPlayer player = setupPlayer(renderersFactory);
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
TestAnalyticsListener listener =
|
player.play();
|
||||||
runAnalyticsTest(
|
player.setMediaSource(new ConcatenatingMediaSource(source0, source1));
|
||||||
new ConcatenatingMediaSource(source0, source1),
|
player.prepare();
|
||||||
/* actionSchedule= */ null,
|
runUntilError(player);
|
||||||
renderersFactory);
|
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period1);
|
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period1);
|
||||||
@ -1660,12 +1702,14 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ExoPlayer player = setupPlayer(renderersFactory);
|
||||||
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
|
player.addAnalyticsListener(listener);
|
||||||
|
|
||||||
TestAnalyticsListener listener =
|
player.play();
|
||||||
runAnalyticsTest(
|
player.setMediaSource(new ConcatenatingMediaSource(source, source));
|
||||||
new ConcatenatingMediaSource(source, source),
|
player.prepare();
|
||||||
/* actionSchedule= */ null,
|
runUntilError(player);
|
||||||
renderersFactory);
|
|
||||||
|
|
||||||
populateEventIds(listener.lastReportedTimeline);
|
populateEventIds(listener.lastReportedTimeline);
|
||||||
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period1);
|
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period1);
|
||||||
@ -1673,11 +1717,7 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onEvents_isReportedWithCorrectEventTimes() throws Exception {
|
public void onEvents_isReportedWithCorrectEventTimes() throws Exception {
|
||||||
ExoPlayer player =
|
ExoPlayer player = setupPlayer();
|
||||||
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build();
|
|
||||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
|
||||||
player.setVideoSurface(surface);
|
|
||||||
|
|
||||||
AnalyticsListener listener = mock(AnalyticsListener.class);
|
AnalyticsListener listener = mock(AnalyticsListener.class);
|
||||||
Format[] formats =
|
Format[] formats =
|
||||||
new Format[] {
|
new Format[] {
|
||||||
@ -1690,20 +1730,18 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
player.setMediaSource(new FakeMediaSource(new FakeTimeline(), formats));
|
player.setMediaSource(new FakeMediaSource(new FakeTimeline(), formats));
|
||||||
player.seekTo(2_000);
|
player.seekTo(2_000);
|
||||||
player.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2.0f));
|
player.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2.0f));
|
||||||
ShadowLooper.runMainLooperToNextTask();
|
runMainLooperToNextTask();
|
||||||
|
|
||||||
// Move to another item and fail with a third one to trigger events with different EventTimes.
|
// Move to another item and fail with a third one to trigger events with different EventTimes.
|
||||||
player.prepare();
|
player.prepare();
|
||||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
player.addMediaSource(new FakeMediaSource(new FakeTimeline(), formats));
|
player.addMediaSource(new FakeMediaSource(new FakeTimeline(), formats));
|
||||||
player.play();
|
player.play();
|
||||||
TestPlayerRunHelper.runUntilPositionDiscontinuity(
|
TestPlayerRunHelper.runUntilPositionDiscontinuity(
|
||||||
player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
|
player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
|
||||||
player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4"));
|
player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4"));
|
||||||
TestPlayerRunHelper.runUntilError(player);
|
runUntilError(player);
|
||||||
ShadowLooper.runMainLooperToNextTask();
|
runMainLooperToNextTask();
|
||||||
player.release();
|
player.release();
|
||||||
surface.release();
|
|
||||||
|
|
||||||
// Verify that expected individual callbacks have been called and capture EventTimes.
|
// Verify that expected individual callbacks have been called and capture EventTimes.
|
||||||
ArgumentCaptor<AnalyticsListener.EventTime> individualTimelineChangedEventTimes =
|
ArgumentCaptor<AnalyticsListener.EventTime> individualTimelineChangedEventTimes =
|
||||||
@ -1928,48 +1966,6 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
.inOrder();
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateEventIds(Timeline timeline) {
|
|
||||||
period0 =
|
|
||||||
new EventWindowAndPeriodId(
|
|
||||||
/* windowIndex= */ 0,
|
|
||||||
new MediaPeriodId(
|
|
||||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
|
|
||||||
period0Seq0 = period0;
|
|
||||||
period0Seq1 =
|
|
||||||
new EventWindowAndPeriodId(
|
|
||||||
/* windowIndex= */ 0,
|
|
||||||
new MediaPeriodId(
|
|
||||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1));
|
|
||||||
window1Period0Seq1 =
|
|
||||||
new EventWindowAndPeriodId(
|
|
||||||
/* windowIndex= */ 1,
|
|
||||||
new MediaPeriodId(
|
|
||||||
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1));
|
|
||||||
if (timeline.getPeriodCount() > 1) {
|
|
||||||
period1 =
|
|
||||||
new EventWindowAndPeriodId(
|
|
||||||
/* windowIndex= */ 1,
|
|
||||||
new MediaPeriodId(
|
|
||||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
|
|
||||||
period1Seq1 = period1;
|
|
||||||
period1Seq0 =
|
|
||||||
new EventWindowAndPeriodId(
|
|
||||||
/* windowIndex= */ 1,
|
|
||||||
new MediaPeriodId(
|
|
||||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
|
|
||||||
period1Seq2 =
|
|
||||||
new EventWindowAndPeriodId(
|
|
||||||
/* windowIndex= */ 1,
|
|
||||||
new MediaPeriodId(
|
|
||||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 2));
|
|
||||||
window0Period1Seq0 =
|
|
||||||
new EventWindowAndPeriodId(
|
|
||||||
/* windowIndex= */ 0,
|
|
||||||
new MediaPeriodId(
|
|
||||||
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void recursiveListenerInvocation_arrivesInCorrectOrder() {
|
public void recursiveListenerInvocation_arrivesInCorrectOrder() {
|
||||||
AnalyticsCollector analyticsCollector = new DefaultAnalyticsCollector(Clock.DEFAULT);
|
AnalyticsCollector analyticsCollector = new DefaultAnalyticsCollector(Clock.DEFAULT);
|
||||||
@ -2027,13 +2023,12 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
exoPlayer.setMediaSource(
|
exoPlayer.setMediaSource(
|
||||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
|
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
|
||||||
exoPlayer.prepare();
|
exoPlayer.prepare();
|
||||||
TestPlayerRunHelper.runUntilPlaybackState(exoPlayer, Player.STATE_READY);
|
runUntilPlaybackState(exoPlayer, Player.STATE_READY);
|
||||||
|
|
||||||
// Release and add delay on releasing thread to verify timestamps of events.
|
// Release and add delay on releasing thread to verify timestamps of events.
|
||||||
exoPlayer.release();
|
exoPlayer.release();
|
||||||
long releaseTimeMs = fakeClock.currentTimeMillis();
|
long releaseTimeMs = fakeClock.currentTimeMillis();
|
||||||
fakeClock.advanceTime(1);
|
fakeClock.advanceTime(1);
|
||||||
ShadowLooper.idleMainLooper();
|
idleMainLooper();
|
||||||
|
|
||||||
// Verify video disable events and release events arrived in order.
|
// Verify video disable events and release events arrived in order.
|
||||||
ArgumentCaptor<AnalyticsListener.EventTime> videoDisabledEventTime =
|
ArgumentCaptor<AnalyticsListener.EventTime> videoDisabledEventTime =
|
||||||
@ -2059,49 +2054,79 @@ public final class DefaultAnalyticsCollectorTest {
|
|||||||
assertThat(releasedEventTime.getValue().realtimeMs).isGreaterThan(videoDisableTimeMs);
|
assertThat(releasedEventTime.getValue().realtimeMs).isGreaterThan(videoDisableTimeMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TestAnalyticsListener runAnalyticsTest(MediaSource mediaSource) throws Exception {
|
private void populateEventIds(Timeline timeline) {
|
||||||
return runAnalyticsTest(mediaSource, /* actionSchedule= */ null);
|
period0 =
|
||||||
|
new EventWindowAndPeriodId(
|
||||||
|
/* windowIndex= */ 0,
|
||||||
|
new MediaPeriodId(
|
||||||
|
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
|
||||||
|
period0Seq0 = period0;
|
||||||
|
period0Seq1 =
|
||||||
|
new EventWindowAndPeriodId(
|
||||||
|
/* windowIndex= */ 0,
|
||||||
|
new MediaPeriodId(
|
||||||
|
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1));
|
||||||
|
window1Period0Seq1 =
|
||||||
|
new EventWindowAndPeriodId(
|
||||||
|
/* windowIndex= */ 1,
|
||||||
|
new MediaPeriodId(
|
||||||
|
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1));
|
||||||
|
if (timeline.getPeriodCount() > 1) {
|
||||||
|
period1 =
|
||||||
|
new EventWindowAndPeriodId(
|
||||||
|
/* windowIndex= */ 1,
|
||||||
|
new MediaPeriodId(
|
||||||
|
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
|
||||||
|
period1Seq1 = period1;
|
||||||
|
period1Seq0 =
|
||||||
|
new EventWindowAndPeriodId(
|
||||||
|
/* windowIndex= */ 1,
|
||||||
|
new MediaPeriodId(
|
||||||
|
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
|
||||||
|
period1Seq2 =
|
||||||
|
new EventWindowAndPeriodId(
|
||||||
|
/* windowIndex= */ 1,
|
||||||
|
new MediaPeriodId(
|
||||||
|
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 2));
|
||||||
|
window0Period1Seq0 =
|
||||||
|
new EventWindowAndPeriodId(
|
||||||
|
/* windowIndex= */ 0,
|
||||||
|
new MediaPeriodId(
|
||||||
|
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TestAnalyticsListener runAnalyticsTest(
|
private static ExoPlayer setupPlayer() {
|
||||||
MediaSource mediaSource, @Nullable ActionSchedule actionSchedule) throws Exception {
|
Clock clock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||||
RenderersFactory renderersFactory =
|
return setupPlayer(
|
||||||
(eventHandler,
|
/* renderersFactory= */ (eventHandler,
|
||||||
videoRendererEventListener,
|
videoRendererEventListener,
|
||||||
audioRendererEventListener,
|
audioRendererEventListener,
|
||||||
textRendererOutput,
|
textRendererOutput,
|
||||||
metadataRendererOutput) ->
|
metadataRendererOutput) -> {
|
||||||
new Renderer[] {
|
HandlerWrapper clockAwareHandler =
|
||||||
new FakeVideoRenderer(eventHandler, videoRendererEventListener),
|
clock.createHandler(eventHandler.getLooper(), /* callback= */ null);
|
||||||
new FakeAudioRenderer(eventHandler, audioRendererEventListener)
|
return new Renderer[] {
|
||||||
};
|
new FakeVideoRenderer(clockAwareHandler, videoRendererEventListener),
|
||||||
return runAnalyticsTest(mediaSource, actionSchedule, renderersFactory);
|
new FakeAudioRenderer(clockAwareHandler, audioRendererEventListener)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TestAnalyticsListener runAnalyticsTest(
|
private static ExoPlayer setupPlayer(RenderersFactory renderersFactory) {
|
||||||
MediaSource mediaSource,
|
return setupPlayer(renderersFactory, new FakeClock(/* isAutoAdvancing= */ true));
|
||||||
@Nullable ActionSchedule actionSchedule,
|
}
|
||||||
RenderersFactory renderersFactory)
|
|
||||||
throws Exception {
|
private static ExoPlayer setupPlayer(RenderersFactory renderersFactory, Clock clock) {
|
||||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
TestAnalyticsListener listener = new TestAnalyticsListener();
|
ExoPlayer player =
|
||||||
try {
|
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
||||||
new ExoPlayerTestRunner.Builder(ApplicationProvider.getApplicationContext())
|
.setClock(clock)
|
||||||
.setMediaSources(mediaSource)
|
.setRenderersFactory(renderersFactory)
|
||||||
.setRenderersFactory(renderersFactory)
|
.build();
|
||||||
.setVideoSurface(surface)
|
player.setVideoSurface(surface);
|
||||||
.setAnalyticsListener(listener)
|
return player;
|
||||||
.setActionSchedule(actionSchedule)
|
|
||||||
.build()
|
|
||||||
.start()
|
|
||||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
|
||||||
.blockUntilEnded(TIMEOUT_MS);
|
|
||||||
} catch (ExoPlaybackException e) {
|
|
||||||
// Ignore ExoPlaybackException as these may be expected.
|
|
||||||
} finally {
|
|
||||||
surface.release();
|
|
||||||
}
|
|
||||||
return listener;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class EventWindowAndPeriodId {
|
private static final class EventWindowAndPeriodId {
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package androidx.media3.test.utils;
|
package androidx.media3.test.utils;
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.DecoderCounters;
|
import androidx.media3.exoplayer.DecoderCounters;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
@ -29,13 +29,15 @@ import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class FakeAudioRenderer extends FakeRenderer {
|
public class FakeAudioRenderer extends FakeRenderer {
|
||||||
|
|
||||||
private final AudioRendererEventListener.EventDispatcher eventDispatcher;
|
private final HandlerWrapper handler;
|
||||||
|
private final AudioRendererEventListener eventListener;
|
||||||
private final DecoderCounters decoderCounters;
|
private final DecoderCounters decoderCounters;
|
||||||
private boolean notifiedPositionAdvancing;
|
private boolean notifiedPositionAdvancing;
|
||||||
|
|
||||||
public FakeAudioRenderer(Handler handler, AudioRendererEventListener eventListener) {
|
public FakeAudioRenderer(HandlerWrapper handler, AudioRendererEventListener eventListener) {
|
||||||
super(C.TRACK_TYPE_AUDIO);
|
super(C.TRACK_TYPE_AUDIO);
|
||||||
eventDispatcher = new AudioRendererEventListener.EventDispatcher(handler, eventListener);
|
this.handler = handler;
|
||||||
|
this.eventListener = eventListener;
|
||||||
decoderCounters = new DecoderCounters();
|
decoderCounters = new DecoderCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,30 +45,33 @@ public class FakeAudioRenderer extends FakeRenderer {
|
|||||||
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
|
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
super.onEnabled(joining, mayRenderStartOfStream);
|
super.onEnabled(joining, mayRenderStartOfStream);
|
||||||
eventDispatcher.enabled(decoderCounters);
|
handler.post(() -> eventListener.onAudioEnabled(decoderCounters));
|
||||||
notifiedPositionAdvancing = false;
|
notifiedPositionAdvancing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
super.onDisabled();
|
super.onDisabled();
|
||||||
eventDispatcher.disabled(decoderCounters);
|
handler.post(() -> eventListener.onAudioDisabled(decoderCounters));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFormatChanged(Format format) {
|
protected void onFormatChanged(Format format) {
|
||||||
eventDispatcher.inputFormatChanged(format, /* decoderReuseEvaluation= */ null);
|
handler.post(
|
||||||
eventDispatcher.decoderInitialized(
|
() -> eventListener.onAudioInputFormatChanged(format, /* decoderReuseEvaluation= */ null));
|
||||||
/* decoderName= */ "fake.audio.decoder",
|
handler.post(
|
||||||
/* initializedTimestampMs= */ SystemClock.elapsedRealtime(),
|
() ->
|
||||||
/* initializationDurationMs= */ 0);
|
eventListener.onAudioDecoderInitialized(
|
||||||
|
/* decoderName= */ "fake.audio.decoder",
|
||||||
|
/* initializedTimestampMs= */ SystemClock.elapsedRealtime(),
|
||||||
|
/* initializationDurationMs= */ 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs) {
|
protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs) {
|
||||||
boolean shouldProcess = super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs);
|
boolean shouldProcess = super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs);
|
||||||
if (shouldProcess && !notifiedPositionAdvancing) {
|
if (shouldProcess && !notifiedPositionAdvancing) {
|
||||||
eventDispatcher.positionAdvancing(System.currentTimeMillis());
|
handler.post(() -> eventListener.onAudioPositionAdvancing(System.currentTimeMillis()));
|
||||||
notifiedPositionAdvancing = true;
|
notifiedPositionAdvancing = true;
|
||||||
}
|
}
|
||||||
return shouldProcess;
|
return shouldProcess;
|
||||||
|
@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
package androidx.media3.test.utils;
|
package androidx.media3.test.utils;
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.DecoderCounters;
|
import androidx.media3.exoplayer.DecoderCounters;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
@ -34,7 +34,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class FakeVideoRenderer extends FakeRenderer {
|
public class FakeVideoRenderer extends FakeRenderer {
|
||||||
|
|
||||||
private final VideoRendererEventListener.EventDispatcher eventDispatcher;
|
private final HandlerWrapper handler;
|
||||||
|
private final VideoRendererEventListener eventListener;
|
||||||
private final DecoderCounters decoderCounters;
|
private final DecoderCounters decoderCounters;
|
||||||
private @MonotonicNonNull Format format;
|
private @MonotonicNonNull Format format;
|
||||||
@Nullable private Object output;
|
@Nullable private Object output;
|
||||||
@ -43,9 +44,10 @@ public class FakeVideoRenderer extends FakeRenderer {
|
|||||||
private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
|
private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
|
||||||
private boolean renderedFirstFrameAfterEnable;
|
private boolean renderedFirstFrameAfterEnable;
|
||||||
|
|
||||||
public FakeVideoRenderer(Handler handler, VideoRendererEventListener eventListener) {
|
public FakeVideoRenderer(HandlerWrapper handler, VideoRendererEventListener eventListener) {
|
||||||
super(C.TRACK_TYPE_VIDEO);
|
super(C.TRACK_TYPE_VIDEO);
|
||||||
eventDispatcher = new VideoRendererEventListener.EventDispatcher(handler, eventListener);
|
this.handler = handler;
|
||||||
|
this.eventListener = eventListener;
|
||||||
decoderCounters = new DecoderCounters();
|
decoderCounters = new DecoderCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ public class FakeVideoRenderer extends FakeRenderer {
|
|||||||
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
|
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
super.onEnabled(joining, mayRenderStartOfStream);
|
super.onEnabled(joining, mayRenderStartOfStream);
|
||||||
eventDispatcher.enabled(decoderCounters);
|
handler.post(() -> eventListener.onVideoEnabled(decoderCounters));
|
||||||
mayRenderFirstFrameAfterEnableIfNotStarted = mayRenderStartOfStream;
|
mayRenderFirstFrameAfterEnableIfNotStarted = mayRenderStartOfStream;
|
||||||
renderedFirstFrameAfterEnable = false;
|
renderedFirstFrameAfterEnable = false;
|
||||||
}
|
}
|
||||||
@ -69,15 +71,17 @@ public class FakeVideoRenderer extends FakeRenderer {
|
|||||||
@Override
|
@Override
|
||||||
protected void onStopped() {
|
protected void onStopped() {
|
||||||
super.onStopped();
|
super.onStopped();
|
||||||
eventDispatcher.droppedFrames(/* droppedFrameCount= */ 0, /* elapsedMs= */ 0);
|
handler.post(() -> eventListener.onDroppedFrames(/* count= */ 0, /* elapsedMs= */ 0));
|
||||||
eventDispatcher.reportVideoFrameProcessingOffset(
|
handler.post(
|
||||||
/* totalProcessingOffsetUs= */ 400000, /* frameCount= */ 10);
|
() ->
|
||||||
|
eventListener.onVideoFrameProcessingOffset(
|
||||||
|
/* totalProcessingOffsetUs= */ 400000, /* frameCount= */ 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
super.onDisabled();
|
super.onDisabled();
|
||||||
eventDispatcher.disabled(decoderCounters);
|
handler.post(() -> eventListener.onVideoDisabled(decoderCounters));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -88,11 +92,14 @@ public class FakeVideoRenderer extends FakeRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFormatChanged(Format format) {
|
protected void onFormatChanged(Format format) {
|
||||||
eventDispatcher.inputFormatChanged(format, /* decoderReuseEvaluation= */ null);
|
handler.post(
|
||||||
eventDispatcher.decoderInitialized(
|
() -> eventListener.onVideoInputFormatChanged(format, /* decoderReuseEvaluation= */ null));
|
||||||
/* decoderName= */ "fake.video.decoder",
|
handler.post(
|
||||||
/* initializedTimestampMs= */ SystemClock.elapsedRealtime(),
|
() ->
|
||||||
/* initializationDurationMs= */ 0);
|
eventListener.onVideoDecoderInitialized(
|
||||||
|
/* decoderName= */ "fake.video.decoder",
|
||||||
|
/* initializedTimestampMs= */ SystemClock.elapsedRealtime(),
|
||||||
|
/* initializationDurationMs= */ 0));
|
||||||
this.format = format;
|
this.format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,10 +140,18 @@ public class FakeVideoRenderer extends FakeRenderer {
|
|||||||
@Nullable Object output = this.output;
|
@Nullable Object output = this.output;
|
||||||
if (shouldProcess && !renderedFirstFrameAfterReset && output != null) {
|
if (shouldProcess && !renderedFirstFrameAfterReset && output != null) {
|
||||||
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format);
|
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format);
|
||||||
eventDispatcher.videoSizeChanged(
|
handler.post(
|
||||||
new VideoSize(
|
() ->
|
||||||
format.width, format.height, format.rotationDegrees, format.pixelWidthHeightRatio));
|
eventListener.onVideoSizeChanged(
|
||||||
eventDispatcher.renderedFirstFrame(output);
|
new VideoSize(
|
||||||
|
format.width,
|
||||||
|
format.height,
|
||||||
|
format.rotationDegrees,
|
||||||
|
format.pixelWidthHeightRatio)));
|
||||||
|
handler.post(
|
||||||
|
() ->
|
||||||
|
eventListener.onRenderedFirstFrame(
|
||||||
|
output, /* renderTimeMs= */ SystemClock.elapsedRealtime()));
|
||||||
renderedFirstFrameAfterReset = true;
|
renderedFirstFrameAfterReset = true;
|
||||||
renderedFirstFrameAfterEnable = true;
|
renderedFirstFrameAfterEnable = true;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.DefaultLoadControl;
|
import androidx.media3.exoplayer.DefaultLoadControl;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
@ -299,13 +300,16 @@ public class TestExoPlayerBuilder {
|
|||||||
videoRendererEventListener,
|
videoRendererEventListener,
|
||||||
audioRendererEventListener,
|
audioRendererEventListener,
|
||||||
textRendererOutput,
|
textRendererOutput,
|
||||||
metadataRendererOutput) ->
|
metadataRendererOutput) -> {
|
||||||
renderers != null
|
HandlerWrapper clockAwareHandler =
|
||||||
? renderers
|
clock.createHandler(eventHandler.getLooper(), /* callback= */ null);
|
||||||
: new Renderer[] {
|
return renderers != null
|
||||||
new FakeVideoRenderer(eventHandler, videoRendererEventListener),
|
? renderers
|
||||||
new FakeAudioRenderer(eventHandler, audioRendererEventListener)
|
: new Renderer[] {
|
||||||
};
|
new FakeVideoRenderer(clockAwareHandler, videoRendererEventListener),
|
||||||
|
new FakeAudioRenderer(clockAwareHandler, audioRendererEventListener)
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ExoPlayer.Builder builder =
|
ExoPlayer.Builder builder =
|
||||||
|
@ -91,6 +91,30 @@ public class TestPlayerRunHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs tasks of the main {@link Looper} until {@link Player#isLoading()} matches the expected
|
||||||
|
* value or a playback error occurs.
|
||||||
|
*
|
||||||
|
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
|
||||||
|
*
|
||||||
|
* @param player The {@link Player}.
|
||||||
|
* @param expectedIsLoading The expected value for {@link Player#isLoading()}.
|
||||||
|
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
|
||||||
|
* exceeded.
|
||||||
|
*/
|
||||||
|
public static void runUntilIsLoading(Player player, boolean expectedIsLoading)
|
||||||
|
throws TimeoutException {
|
||||||
|
verifyMainTestThread(player);
|
||||||
|
if (player instanceof ExoPlayer) {
|
||||||
|
verifyPlaybackThreadIsAlive((ExoPlayer) player);
|
||||||
|
}
|
||||||
|
runMainLooperUntil(
|
||||||
|
() -> player.isLoading() == expectedIsLoading || player.getPlayerError() != null);
|
||||||
|
if (player.getPlayerError() != null) {
|
||||||
|
throw new IllegalStateException(player.getPlayerError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs tasks of the main {@link Looper} until {@link Player#getCurrentTimeline()} matches the
|
* Runs tasks of the main {@link Looper} until {@link Player#getCurrentTimeline()} matches the
|
||||||
* expected timeline or a playback error occurs.
|
* expected timeline or a playback error occurs.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user