From 59331c3c887ce8d42b76aa2e1c46106028f862dc Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 24 Jul 2019 15:34:07 +0100 Subject: [PATCH] Report mediaPeriodCreated/Released in MaskingMediaSource. Creating a period in MaskingMediaSource may result in delayed event reporting depending on when the actual period gets created. To avoid event reporting inaccuracies, report the mediaPeriodCreated and mediaPeriodReleased events directly. Issue:#5407 PiperOrigin-RevId: 259737170 --- .../source/CompositeMediaSource.java | 30 +++++++++++++++---- .../exoplayer2/source/MaskingMediaSource.java | 21 +++++++++++-- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java index 3672c304cc..4ebe97313b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java @@ -96,11 +96,11 @@ public abstract class CompositeMediaSource extends BaseMediaSource { /** * Prepares a child source. * - *

{@link #onChildSourceInfoRefreshed(Object, MediaSource, Timeline)} will be called when the - * child source updates its timeline with the same {@code id} passed to this method. + *

{@link #onChildSourceInfoRefreshed(T, MediaSource, Timeline)} will be called when the child + * source updates its timeline with the same {@code id} passed to this method. * - *

Any child sources that aren't explicitly released with {@link #releaseChildSource(Object)} - * will be released in {@link #releaseSourceInternal()}. + *

Any child sources that aren't explicitly released with {@link #releaseChildSource(T)} will + * be released in {@link #releaseSourceInternal()}. * * @param id A unique id to identify the child source preparation. Null is allowed as an id. * @param mediaSource The child {@link MediaSource}. @@ -188,6 +188,18 @@ public abstract class CompositeMediaSource extends BaseMediaSource { return mediaTimeMs; } + /** + * Returns whether {@link MediaSourceEventListener#onMediaPeriodCreated(int, MediaPeriodId)} and + * {@link MediaSourceEventListener#onMediaPeriodReleased(int, MediaPeriodId)} events of the given + * media period should be reported. The default implementation is to always report these events. + * + * @param mediaPeriodId A {@link MediaPeriodId} in the composite media source. + * @return Whether create and release events for this media period should be reported. + */ + protected boolean shouldDispatchCreateOrReleaseEvent(MediaPeriodId mediaPeriodId) { + return true; + } + private static final class MediaSourceAndListener { public final MediaSource mediaSource; @@ -215,14 +227,20 @@ public abstract class CompositeMediaSource extends BaseMediaSource { @Override public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.mediaPeriodCreated(); + if (shouldDispatchCreateOrReleaseEvent( + Assertions.checkNotNull(eventDispatcher.mediaPeriodId))) { + eventDispatcher.mediaPeriodCreated(); + } } } @Override public void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.mediaPeriodReleased(); + if (shouldDispatchCreateOrReleaseEvent( + Assertions.checkNotNull(eventDispatcher.mediaPeriodId))) { + eventDispatcher.mediaPeriodReleased(); + } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java index 1fca824910..d9dd83de4f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java @@ -19,8 +19,10 @@ import androidx.annotation.Nullable; import android.util.Pair; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -37,6 +39,7 @@ public final class MaskingMediaSource extends CompositeMediaSource { private MaskingTimeline timeline; @Nullable private MaskingMediaPeriod unpreparedMaskingMediaPeriod; + @Nullable private EventDispatcher unpreparedMaskingMediaPeriodEventDispatcher; private boolean hasStartedPreparing; private boolean isPrepared; @@ -96,6 +99,9 @@ public final class MaskingMediaSource extends CompositeMediaSource { // unset and we don't load beyond periods with unset duration. We need to figure out how to // handle the prepare positions of multiple deferred media periods, should that ever change. unpreparedMaskingMediaPeriod = mediaPeriod; + unpreparedMaskingMediaPeriodEventDispatcher = + createEventDispatcher(/* windowIndex= */ 0, id, /* mediaTimeOffsetMs= */ 0); + unpreparedMaskingMediaPeriodEventDispatcher.mediaPeriodCreated(); if (!hasStartedPreparing) { hasStartedPreparing = true; prepareChildSource(/* id= */ null, mediaSource); @@ -107,7 +113,11 @@ public final class MaskingMediaSource extends CompositeMediaSource { @Override public void releasePeriod(MediaPeriod mediaPeriod) { ((MaskingMediaPeriod) mediaPeriod).releasePeriod(); - unpreparedMaskingMediaPeriod = null; + if (mediaPeriod == unpreparedMaskingMediaPeriod) { + Assertions.checkNotNull(unpreparedMaskingMediaPeriodEventDispatcher).mediaPeriodReleased(); + unpreparedMaskingMediaPeriodEventDispatcher = null; + unpreparedMaskingMediaPeriod = null; + } } @Override @@ -154,7 +164,6 @@ public final class MaskingMediaSource extends CompositeMediaSource { timeline = MaskingTimeline.createWithRealTimeline(newTimeline, periodUid); if (unpreparedMaskingMediaPeriod != null) { MaskingMediaPeriod maskingPeriod = unpreparedMaskingMediaPeriod; - unpreparedMaskingMediaPeriod = null; maskingPeriod.overridePreparePositionUs(periodPositionUs); MediaPeriodId idInSource = maskingPeriod.id.copyWithPeriodUid(getInternalPeriodUid(maskingPeriod.id.periodUid)); @@ -172,6 +181,14 @@ public final class MaskingMediaSource extends CompositeMediaSource { return mediaPeriodId.copyWithPeriodUid(getExternalPeriodUid(mediaPeriodId.periodUid)); } + @Override + protected boolean shouldDispatchCreateOrReleaseEvent(MediaPeriodId mediaPeriodId) { + // Suppress create and release events for the period created while the source was still + // unprepared, as we send these events from this class. + return unpreparedMaskingMediaPeriod == null + || !mediaPeriodId.equals(unpreparedMaskingMediaPeriod.id); + } + private Object getInternalPeriodUid(Object externalPeriodUid) { return externalPeriodUid.equals(MaskingTimeline.DUMMY_EXTERNAL_ID) ? timeline.replacedInternalId