mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
add default methods isSingleWindow and getInitialTimeline to MediaSource interface
PiperOrigin-RevId: 277695826
This commit is contained in:
parent
df251ad1be
commit
01a4cf98d5
7 changed files with 352 additions and 25 deletions
|
|
@ -19,6 +19,7 @@ import android.util.Pair;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flexible representation of the structure of media. A timeline is able to represent the
|
* A flexible representation of the structure of media. A timeline is able to represent the
|
||||||
|
|
@ -278,6 +279,48 @@ public abstract class Timeline {
|
||||||
return positionInFirstPeriodUs;
|
return positionInFirstPeriodUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || !getClass().equals(obj.getClass())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Window that = (Window) obj;
|
||||||
|
return Util.areEqual(uid, that.uid)
|
||||||
|
&& Util.areEqual(tag, that.tag)
|
||||||
|
&& Util.areEqual(manifest, that.manifest)
|
||||||
|
&& presentationStartTimeMs == that.presentationStartTimeMs
|
||||||
|
&& windowStartTimeMs == that.windowStartTimeMs
|
||||||
|
&& isSeekable == that.isSeekable
|
||||||
|
&& isDynamic == that.isDynamic
|
||||||
|
&& isLive == that.isLive
|
||||||
|
&& defaultPositionUs == that.defaultPositionUs
|
||||||
|
&& durationUs == that.durationUs
|
||||||
|
&& firstPeriodIndex == that.firstPeriodIndex
|
||||||
|
&& lastPeriodIndex == that.lastPeriodIndex
|
||||||
|
&& positionInFirstPeriodUs == that.positionInFirstPeriodUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 7;
|
||||||
|
result = 31 * result + uid.hashCode();
|
||||||
|
result = 31 * result + (tag == null ? 0 : tag.hashCode());
|
||||||
|
result = 31 * result + (manifest == null ? 0 : manifest.hashCode());
|
||||||
|
result = 31 * result + (int) (presentationStartTimeMs ^ (presentationStartTimeMs >>> 32));
|
||||||
|
result = 31 * result + (int) (windowStartTimeMs ^ (windowStartTimeMs >>> 32));
|
||||||
|
result = 31 * result + (isSeekable ? 1 : 0);
|
||||||
|
result = 31 * result + (isDynamic ? 1 : 0);
|
||||||
|
result = 31 * result + (isLive ? 1 : 0);
|
||||||
|
result = 31 * result + (int) (defaultPositionUs ^ (defaultPositionUs >>> 32));
|
||||||
|
result = 31 * result + (int) (durationUs ^ (durationUs >>> 32));
|
||||||
|
result = 31 * result + firstPeriodIndex;
|
||||||
|
result = 31 * result + lastPeriodIndex;
|
||||||
|
result = 31 * result + (int) (positionInFirstPeriodUs ^ (positionInFirstPeriodUs >>> 32));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -534,6 +577,34 @@ public abstract class Timeline {
|
||||||
return adPlaybackState.adResumePositionUs;
|
return adPlaybackState.adResumePositionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || !getClass().equals(obj.getClass())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Period that = (Period) obj;
|
||||||
|
return Util.areEqual(id, that.id)
|
||||||
|
&& Util.areEqual(uid, that.uid)
|
||||||
|
&& windowIndex == that.windowIndex
|
||||||
|
&& durationUs == that.durationUs
|
||||||
|
&& positionInWindowUs == that.positionInWindowUs
|
||||||
|
&& Util.areEqual(adPlaybackState, that.adPlaybackState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 7;
|
||||||
|
result = 31 * result + (id == null ? 0 : id.hashCode());
|
||||||
|
result = 31 * result + (uid == null ? 0 : uid.hashCode());
|
||||||
|
result = 31 * result + windowIndex;
|
||||||
|
result = 31 * result + (int) (durationUs ^ (durationUs >>> 32));
|
||||||
|
result = 31 * result + (int) (positionInWindowUs ^ (positionInWindowUs >>> 32));
|
||||||
|
result = 31 * result + (adPlaybackState == null ? 0 : adPlaybackState.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An empty timeline. */
|
/** An empty timeline. */
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,23 @@ public final class ConcatenatingMediaSource extends CompositeMediaSource<MediaSo
|
||||||
addMediaSources(Arrays.asList(mediaSources));
|
addMediaSources(Arrays.asList(mediaSources));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Timeline getInitialTimeline() {
|
||||||
|
ShuffleOrder shuffleOrder =
|
||||||
|
this.shuffleOrder.getLength() != mediaSourcesPublic.size()
|
||||||
|
? this.shuffleOrder
|
||||||
|
.cloneAndClear()
|
||||||
|
.cloneAndInsert(
|
||||||
|
/* insertionIndex= */ 0, /* insertionCount= */ mediaSourcesPublic.size())
|
||||||
|
: this.shuffleOrder;
|
||||||
|
return new ConcatenatedTimeline(mediaSourcesPublic, shuffleOrder, isAtomic);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSingleWindow() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends a {@link MediaSource} to the playlist.
|
* Appends a {@link MediaSource} to the playlist.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public final class LoopingMediaSource extends CompositeMediaSource<Void> {
|
public final class LoopingMediaSource extends CompositeMediaSource<Void> {
|
||||||
|
|
||||||
private final MediaSource childSource;
|
private final MaskingMediaSource maskingMediaSource;
|
||||||
private final int loopCount;
|
private final int loopCount;
|
||||||
private final Map<MediaPeriodId, MediaPeriodId> childMediaPeriodIdToMediaPeriodId;
|
private final Map<MediaPeriodId, MediaPeriodId> childMediaPeriodIdToMediaPeriodId;
|
||||||
private final Map<MediaPeriod, MediaPeriodId> mediaPeriodToChildMediaPeriodId;
|
private final Map<MediaPeriod, MediaPeriodId> mediaPeriodToChildMediaPeriodId;
|
||||||
|
|
@ -58,7 +58,7 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
|
||||||
*/
|
*/
|
||||||
public LoopingMediaSource(MediaSource childSource, int loopCount) {
|
public LoopingMediaSource(MediaSource childSource, int loopCount) {
|
||||||
Assertions.checkArgument(loopCount > 0);
|
Assertions.checkArgument(loopCount > 0);
|
||||||
this.childSource = childSource;
|
this.maskingMediaSource = new MaskingMediaSource(childSource, /* useLazyPreparation= */ false);
|
||||||
this.loopCount = loopCount;
|
this.loopCount = loopCount;
|
||||||
childMediaPeriodIdToMediaPeriodId = new HashMap<>();
|
childMediaPeriodIdToMediaPeriodId = new HashMap<>();
|
||||||
mediaPeriodToChildMediaPeriodId = new HashMap<>();
|
mediaPeriodToChildMediaPeriodId = new HashMap<>();
|
||||||
|
|
@ -67,32 +67,45 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Object getTag() {
|
public Object getTag() {
|
||||||
return childSource.getTag();
|
return maskingMediaSource.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Timeline getInitialTimeline() {
|
||||||
|
return loopCount != Integer.MAX_VALUE
|
||||||
|
? new LoopingTimeline(maskingMediaSource.getTimeline(), loopCount)
|
||||||
|
: new InfinitelyLoopingTimeline(maskingMediaSource.getTimeline());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSingleWindow() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
||||||
super.prepareSourceInternal(mediaTransferListener);
|
super.prepareSourceInternal(mediaTransferListener);
|
||||||
prepareChildSource(/* id= */ null, childSource);
|
prepareChildSource(/* id= */ null, maskingMediaSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||||
if (loopCount == Integer.MAX_VALUE) {
|
if (loopCount == Integer.MAX_VALUE) {
|
||||||
return childSource.createPeriod(id, allocator, startPositionUs);
|
return maskingMediaSource.createPeriod(id, allocator, startPositionUs);
|
||||||
}
|
}
|
||||||
Object childPeriodUid = LoopingTimeline.getChildPeriodUidFromConcatenatedUid(id.periodUid);
|
Object childPeriodUid = LoopingTimeline.getChildPeriodUidFromConcatenatedUid(id.periodUid);
|
||||||
MediaPeriodId childMediaPeriodId = id.copyWithPeriodUid(childPeriodUid);
|
MediaPeriodId childMediaPeriodId = id.copyWithPeriodUid(childPeriodUid);
|
||||||
childMediaPeriodIdToMediaPeriodId.put(childMediaPeriodId, id);
|
childMediaPeriodIdToMediaPeriodId.put(childMediaPeriodId, id);
|
||||||
MediaPeriod mediaPeriod =
|
MediaPeriod mediaPeriod =
|
||||||
childSource.createPeriod(childMediaPeriodId, allocator, startPositionUs);
|
maskingMediaSource.createPeriod(childMediaPeriodId, allocator, startPositionUs);
|
||||||
mediaPeriodToChildMediaPeriodId.put(mediaPeriod, childMediaPeriodId);
|
mediaPeriodToChildMediaPeriodId.put(mediaPeriod, childMediaPeriodId);
|
||||||
return mediaPeriod;
|
return mediaPeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releasePeriod(MediaPeriod mediaPeriod) {
|
public void releasePeriod(MediaPeriod mediaPeriod) {
|
||||||
childSource.releasePeriod(mediaPeriod);
|
maskingMediaSource.releasePeriod(mediaPeriod);
|
||||||
MediaPeriodId childMediaPeriodId = mediaPeriodToChildMediaPeriodId.remove(mediaPeriod);
|
MediaPeriodId childMediaPeriodId = mediaPeriodToChildMediaPeriodId.remove(mediaPeriod);
|
||||||
if (childMediaPeriodId != null) {
|
if (childMediaPeriodId != null) {
|
||||||
childMediaPeriodIdToMediaPeriodId.remove(childMediaPeriodId);
|
childMediaPeriodIdToMediaPeriodId.remove(childMediaPeriodId);
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
@Nullable private EventDispatcher unpreparedMaskingMediaPeriodEventDispatcher;
|
@Nullable private EventDispatcher unpreparedMaskingMediaPeriodEventDispatcher;
|
||||||
private boolean hasStartedPreparing;
|
private boolean hasStartedPreparing;
|
||||||
private boolean isPrepared;
|
private boolean isPrepared;
|
||||||
|
private boolean hasRealTimeline;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the masking media source.
|
* Creates the masking media source.
|
||||||
|
|
@ -54,14 +55,22 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
*/
|
*/
|
||||||
public MaskingMediaSource(MediaSource mediaSource, boolean useLazyPreparation) {
|
public MaskingMediaSource(MediaSource mediaSource, boolean useLazyPreparation) {
|
||||||
this.mediaSource = mediaSource;
|
this.mediaSource = mediaSource;
|
||||||
this.useLazyPreparation = useLazyPreparation;
|
this.useLazyPreparation = useLazyPreparation && mediaSource.isSingleWindow();
|
||||||
window = new Timeline.Window();
|
window = new Timeline.Window();
|
||||||
period = new Timeline.Period();
|
period = new Timeline.Period();
|
||||||
|
Timeline initialTimeline = mediaSource.getInitialTimeline();
|
||||||
|
if (initialTimeline != null) {
|
||||||
|
timeline =
|
||||||
|
MaskingTimeline.createWithRealTimeline(
|
||||||
|
initialTimeline, /* firstWindowUid= */ null, /* firstPeriodUid= */ null);
|
||||||
|
hasRealTimeline = true;
|
||||||
|
} else {
|
||||||
timeline = MaskingTimeline.createWithDummyTimeline(mediaSource.getTag());
|
timeline = MaskingTimeline.createWithDummyTimeline(mediaSource.getTag());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the {@link Timeline}. */
|
/** Returns the {@link Timeline}. */
|
||||||
public Timeline getTimeline() {
|
public synchronized Timeline getTimeline() {
|
||||||
return timeline;
|
return timeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,13 +138,15 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onChildSourceInfoRefreshed(
|
protected synchronized void onChildSourceInfoRefreshed(
|
||||||
Void id, MediaSource mediaSource, Timeline newTimeline) {
|
Void id, MediaSource mediaSource, Timeline newTimeline) {
|
||||||
if (isPrepared) {
|
if (isPrepared) {
|
||||||
timeline = timeline.cloneWithUpdatedTimeline(newTimeline);
|
timeline = timeline.cloneWithUpdatedTimeline(newTimeline);
|
||||||
} else if (newTimeline.isEmpty()) {
|
} else if (newTimeline.isEmpty()) {
|
||||||
timeline =
|
timeline =
|
||||||
MaskingTimeline.createWithRealTimeline(
|
hasRealTimeline
|
||||||
|
? timeline.cloneWithUpdatedTimeline(newTimeline)
|
||||||
|
: MaskingTimeline.createWithRealTimeline(
|
||||||
newTimeline, Window.SINGLE_WINDOW_UID, MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID);
|
newTimeline, Window.SINGLE_WINDOW_UID, MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID);
|
||||||
} else {
|
} else {
|
||||||
// Determine first period and the start position.
|
// Determine first period and the start position.
|
||||||
|
|
@ -164,7 +175,10 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
window, period, /* windowIndex= */ 0, windowStartPositionUs);
|
window, period, /* windowIndex= */ 0, windowStartPositionUs);
|
||||||
Object periodUid = periodPosition.first;
|
Object periodUid = periodPosition.first;
|
||||||
long periodPositionUs = periodPosition.second;
|
long periodPositionUs = periodPosition.second;
|
||||||
timeline = MaskingTimeline.createWithRealTimeline(newTimeline, windowUid, periodUid);
|
timeline =
|
||||||
|
hasRealTimeline
|
||||||
|
? timeline.cloneWithUpdatedTimeline(newTimeline)
|
||||||
|
: MaskingTimeline.createWithRealTimeline(newTimeline, windowUid, periodUid);
|
||||||
if (unpreparedMaskingMediaPeriod != null) {
|
if (unpreparedMaskingMediaPeriod != null) {
|
||||||
MaskingMediaPeriod maskingPeriod = unpreparedMaskingMediaPeriod;
|
MaskingMediaPeriod maskingPeriod = unpreparedMaskingMediaPeriod;
|
||||||
maskingPeriod.overridePreparePositionUs(periodPositionUs);
|
maskingPeriod.overridePreparePositionUs(periodPositionUs);
|
||||||
|
|
@ -173,6 +187,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
maskingPeriod.createPeriod(idInSource);
|
maskingPeriod.createPeriod(idInSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hasRealTimeline = true;
|
||||||
isPrepared = true;
|
isPrepared = true;
|
||||||
refreshSourceInfo(this.timeline);
|
refreshSourceInfo(this.timeline);
|
||||||
}
|
}
|
||||||
|
|
@ -193,13 +208,15 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getInternalPeriodUid(Object externalPeriodUid) {
|
private Object getInternalPeriodUid(Object externalPeriodUid) {
|
||||||
return externalPeriodUid.equals(MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID)
|
return timeline.replacedInternalPeriodUid != null
|
||||||
|
&& externalPeriodUid.equals(MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID)
|
||||||
? timeline.replacedInternalPeriodUid
|
? timeline.replacedInternalPeriodUid
|
||||||
: externalPeriodUid;
|
: externalPeriodUid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getExternalPeriodUid(Object internalPeriodUid) {
|
private Object getExternalPeriodUid(Object internalPeriodUid) {
|
||||||
return timeline.replacedInternalPeriodUid.equals(internalPeriodUid)
|
return timeline.replacedInternalPeriodUid != null
|
||||||
|
&& timeline.replacedInternalPeriodUid.equals(internalPeriodUid)
|
||||||
? MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID
|
? MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID
|
||||||
: internalPeriodUid;
|
: internalPeriodUid;
|
||||||
}
|
}
|
||||||
|
|
@ -212,8 +229,8 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
|
|
||||||
public static final Object DUMMY_EXTERNAL_PERIOD_UID = new Object();
|
public static final Object DUMMY_EXTERNAL_PERIOD_UID = new Object();
|
||||||
|
|
||||||
private final Object replacedInternalWindowUid;
|
@Nullable private final Object replacedInternalWindowUid;
|
||||||
private final Object replacedInternalPeriodUid;
|
@Nullable private final Object replacedInternalPeriodUid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an instance with a dummy timeline using the provided window tag.
|
* Returns an instance with a dummy timeline using the provided window tag.
|
||||||
|
|
@ -236,12 +253,14 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
* assigned {@link #DUMMY_EXTERNAL_PERIOD_UID}.
|
* assigned {@link #DUMMY_EXTERNAL_PERIOD_UID}.
|
||||||
*/
|
*/
|
||||||
public static MaskingTimeline createWithRealTimeline(
|
public static MaskingTimeline createWithRealTimeline(
|
||||||
Timeline timeline, Object firstWindowUid, Object firstPeriodUid) {
|
Timeline timeline, @Nullable Object firstWindowUid, @Nullable Object firstPeriodUid) {
|
||||||
return new MaskingTimeline(timeline, firstWindowUid, firstPeriodUid);
|
return new MaskingTimeline(timeline, firstWindowUid, firstPeriodUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MaskingTimeline(
|
private MaskingTimeline(
|
||||||
Timeline timeline, Object replacedInternalWindowUid, Object replacedInternalPeriodUid) {
|
Timeline timeline,
|
||||||
|
@Nullable Object replacedInternalWindowUid,
|
||||||
|
@Nullable Object replacedInternalPeriodUid) {
|
||||||
super(timeline);
|
super(timeline);
|
||||||
this.replacedInternalWindowUid = replacedInternalWindowUid;
|
this.replacedInternalWindowUid = replacedInternalWindowUid;
|
||||||
this.replacedInternalPeriodUid = replacedInternalPeriodUid;
|
this.replacedInternalPeriodUid = replacedInternalPeriodUid;
|
||||||
|
|
@ -273,7 +292,7 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
@Override
|
@Override
|
||||||
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
||||||
timeline.getPeriod(periodIndex, period, setIds);
|
timeline.getPeriod(periodIndex, period, setIds);
|
||||||
if (Util.areEqual(period.uid, replacedInternalPeriodUid)) {
|
if (Util.areEqual(period.uid, replacedInternalPeriodUid) && setIds) {
|
||||||
period.uid = DUMMY_EXTERNAL_PERIOD_UID;
|
period.uid = DUMMY_EXTERNAL_PERIOD_UID;
|
||||||
}
|
}
|
||||||
return period;
|
return period;
|
||||||
|
|
@ -282,7 +301,9 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
@Override
|
@Override
|
||||||
public int getIndexOfPeriod(Object uid) {
|
public int getIndexOfPeriod(Object uid) {
|
||||||
return timeline.getIndexOfPeriod(
|
return timeline.getIndexOfPeriod(
|
||||||
DUMMY_EXTERNAL_PERIOD_UID.equals(uid) ? replacedInternalPeriodUid : uid);
|
DUMMY_EXTERNAL_PERIOD_UID.equals(uid) && replacedInternalPeriodUid != null
|
||||||
|
? replacedInternalPeriodUid
|
||||||
|
: uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -333,8 +354,8 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||||
@Override
|
@Override
|
||||||
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
||||||
return period.set(
|
return period.set(
|
||||||
/* id= */ 0,
|
/* id= */ setIds ? 0 : null,
|
||||||
/* uid= */ MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID,
|
/* uid= */ setIds ? MaskingTimeline.DUMMY_EXTERNAL_PERIOD_UID : null,
|
||||||
/* windowIndex= */ 0,
|
/* windowIndex= */ 0,
|
||||||
/* durationUs = */ C.TIME_UNSET,
|
/* durationUs = */ C.TIME_UNSET,
|
||||||
/* positionInWindowUs= */ 0);
|
/* positionInWindowUs= */ 0);
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,33 @@ public interface MediaSource {
|
||||||
*/
|
*/
|
||||||
void removeEventListener(MediaSourceEventListener eventListener);
|
void removeEventListener(MediaSourceEventListener eventListener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the initial dummy timeline that is returned immediately when the real timeline is not
|
||||||
|
* yet known, or null to let the player create an initial timeline.
|
||||||
|
*
|
||||||
|
* <p>The initial timeline must use the same uids for windows and periods that the real timeline
|
||||||
|
* will use. It also must provide windows which are marked as dynamic to indicate that the window
|
||||||
|
* is expected to change when the real timeline arrives.
|
||||||
|
*
|
||||||
|
* <p>Any media source which has multiple windows should typically provide such an initial
|
||||||
|
* timeline to make sure the player reports the correct number of windows immediately.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default Timeline getInitialTimeline() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the media source is guaranteed to never have zero or more than one window.
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns {@code true}.
|
||||||
|
*
|
||||||
|
* @return true if the source has exactly one window.
|
||||||
|
*/
|
||||||
|
default boolean isSingleWindow() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the tag set on the media source, or null if none was set. */
|
/** Returns the tag set on the media source, or null if none was set. */
|
||||||
@Nullable
|
@Nullable
|
||||||
default Object getTag() {
|
default Object getTag() {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2;
|
package com.google.android.exoplayer2;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
|
|
@ -58,4 +60,148 @@ public class TimelineTest {
|
||||||
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0);
|
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0);
|
||||||
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
|
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWindowEquals() {
|
||||||
|
Timeline.Window window = new Timeline.Window();
|
||||||
|
assertThat(window).isEqualTo(new Timeline.Window());
|
||||||
|
|
||||||
|
Timeline.Window otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.tag = new Object();
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.manifest = new Object();
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.presentationStartTimeMs = C.TIME_UNSET;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.windowStartTimeMs = C.TIME_UNSET;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.isSeekable = true;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.isDynamic = true;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.isLive = true;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.defaultPositionUs = C.TIME_UNSET;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.durationUs = C.TIME_UNSET;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.firstPeriodIndex = 1;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.lastPeriodIndex = 1;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
otherWindow = new Timeline.Window();
|
||||||
|
otherWindow.positionInFirstPeriodUs = C.TIME_UNSET;
|
||||||
|
assertThat(window).isNotEqualTo(otherWindow);
|
||||||
|
|
||||||
|
window.uid = new Object();
|
||||||
|
window.tag = new Object();
|
||||||
|
window.manifest = new Object();
|
||||||
|
window.presentationStartTimeMs = C.TIME_UNSET;
|
||||||
|
window.windowStartTimeMs = C.TIME_UNSET;
|
||||||
|
window.isSeekable = true;
|
||||||
|
window.isDynamic = true;
|
||||||
|
window.isLive = true;
|
||||||
|
window.defaultPositionUs = C.TIME_UNSET;
|
||||||
|
window.durationUs = C.TIME_UNSET;
|
||||||
|
window.firstPeriodIndex = 1;
|
||||||
|
window.lastPeriodIndex = 1;
|
||||||
|
window.positionInFirstPeriodUs = C.TIME_UNSET;
|
||||||
|
otherWindow =
|
||||||
|
otherWindow.set(
|
||||||
|
window.uid,
|
||||||
|
window.tag,
|
||||||
|
window.manifest,
|
||||||
|
window.presentationStartTimeMs,
|
||||||
|
window.windowStartTimeMs,
|
||||||
|
window.isSeekable,
|
||||||
|
window.isDynamic,
|
||||||
|
window.isLive,
|
||||||
|
window.defaultPositionUs,
|
||||||
|
window.durationUs,
|
||||||
|
window.firstPeriodIndex,
|
||||||
|
window.lastPeriodIndex,
|
||||||
|
window.positionInFirstPeriodUs);
|
||||||
|
assertThat(window).isEqualTo(otherWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWindowHashCode() {
|
||||||
|
Timeline.Window window = new Timeline.Window();
|
||||||
|
Timeline.Window otherWindow = new Timeline.Window();
|
||||||
|
assertThat(window.hashCode()).isEqualTo(otherWindow.hashCode());
|
||||||
|
|
||||||
|
window.tag = new Object();
|
||||||
|
assertThat(window.hashCode()).isNotEqualTo(otherWindow.hashCode());
|
||||||
|
otherWindow.tag = window.tag;
|
||||||
|
assertThat(window.hashCode()).isEqualTo(otherWindow.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeriodEquals() {
|
||||||
|
Timeline.Period period = new Timeline.Period();
|
||||||
|
assertThat(period).isEqualTo(new Timeline.Period());
|
||||||
|
|
||||||
|
Timeline.Period otherPeriod = new Timeline.Period();
|
||||||
|
otherPeriod.id = new Object();
|
||||||
|
assertThat(period).isNotEqualTo(otherPeriod);
|
||||||
|
|
||||||
|
otherPeriod = new Timeline.Period();
|
||||||
|
otherPeriod.uid = new Object();
|
||||||
|
assertThat(period).isNotEqualTo(otherPeriod);
|
||||||
|
|
||||||
|
otherPeriod = new Timeline.Period();
|
||||||
|
otherPeriod.windowIndex = 12;
|
||||||
|
assertThat(period).isNotEqualTo(otherPeriod);
|
||||||
|
|
||||||
|
otherPeriod = new Timeline.Period();
|
||||||
|
otherPeriod.durationUs = 11L;
|
||||||
|
assertThat(period).isNotEqualTo(otherPeriod);
|
||||||
|
|
||||||
|
otherPeriod = new Timeline.Period();
|
||||||
|
period.id = new Object();
|
||||||
|
period.uid = new Object();
|
||||||
|
period.windowIndex = 1;
|
||||||
|
period.durationUs = 123L;
|
||||||
|
otherPeriod =
|
||||||
|
otherPeriod.set(
|
||||||
|
period.id,
|
||||||
|
period.uid,
|
||||||
|
period.windowIndex,
|
||||||
|
period.durationUs,
|
||||||
|
/* positionInWindowUs= */ 0);
|
||||||
|
assertThat(period).isEqualTo(otherPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeriodHashCode() {
|
||||||
|
Timeline.Period period = new Timeline.Period();
|
||||||
|
Timeline.Period otherPeriod = new Timeline.Period();
|
||||||
|
assertThat(period.hashCode()).isEqualTo(otherPeriod.hashCode());
|
||||||
|
|
||||||
|
period.windowIndex = 12;
|
||||||
|
assertThat(period.hashCode()).isNotEqualTo(otherPeriod.hashCode());
|
||||||
|
otherPeriod.windowIndex = period.windowIndex;
|
||||||
|
assertThat(period.hashCode()).isEqualTo(otherPeriod.hashCode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ public final class FakeTimeline extends Timeline {
|
||||||
public final Object id;
|
public final Object id;
|
||||||
public final boolean isSeekable;
|
public final boolean isSeekable;
|
||||||
public final boolean isDynamic;
|
public final boolean isDynamic;
|
||||||
|
public final boolean isLive;
|
||||||
public final long durationUs;
|
public final long durationUs;
|
||||||
public final AdPlaybackState adPlaybackState;
|
public final AdPlaybackState adPlaybackState;
|
||||||
|
|
||||||
|
|
@ -99,10 +100,41 @@ public final class FakeTimeline extends Timeline {
|
||||||
boolean isDynamic,
|
boolean isDynamic,
|
||||||
long durationUs,
|
long durationUs,
|
||||||
AdPlaybackState adPlaybackState) {
|
AdPlaybackState adPlaybackState) {
|
||||||
|
this(
|
||||||
|
periodCount,
|
||||||
|
id,
|
||||||
|
isSeekable,
|
||||||
|
isDynamic,
|
||||||
|
/* isLive= */ isDynamic,
|
||||||
|
durationUs,
|
||||||
|
adPlaybackState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a window definition with ad groups.
|
||||||
|
*
|
||||||
|
* @param periodCount The number of periods in the window. Each period get an equal slice of the
|
||||||
|
* total window duration.
|
||||||
|
* @param id The UID of the window.
|
||||||
|
* @param isSeekable Whether the window is seekable.
|
||||||
|
* @param isDynamic Whether the window is dynamic.
|
||||||
|
* @param isLive Whether the window is live.
|
||||||
|
* @param durationUs The duration of the window in microseconds.
|
||||||
|
* @param adPlaybackState The ad playback state.
|
||||||
|
*/
|
||||||
|
public TimelineWindowDefinition(
|
||||||
|
int periodCount,
|
||||||
|
Object id,
|
||||||
|
boolean isSeekable,
|
||||||
|
boolean isDynamic,
|
||||||
|
boolean isLive,
|
||||||
|
long durationUs,
|
||||||
|
AdPlaybackState adPlaybackState) {
|
||||||
this.periodCount = periodCount;
|
this.periodCount = periodCount;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.isSeekable = isSeekable;
|
this.isSeekable = isSeekable;
|
||||||
this.isDynamic = isDynamic;
|
this.isDynamic = isDynamic;
|
||||||
|
this.isLive = isLive;
|
||||||
this.durationUs = durationUs;
|
this.durationUs = durationUs;
|
||||||
this.adPlaybackState = adPlaybackState;
|
this.adPlaybackState = adPlaybackState;
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +221,7 @@ public final class FakeTimeline extends Timeline {
|
||||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||||
windowDefinition.isSeekable,
|
windowDefinition.isSeekable,
|
||||||
windowDefinition.isDynamic,
|
windowDefinition.isDynamic,
|
||||||
/* isLive= */ windowDefinition.isDynamic,
|
windowDefinition.isLive,
|
||||||
/* defaultPositionUs= */ 0,
|
/* defaultPositionUs= */ 0,
|
||||||
windowDefinition.durationUs,
|
windowDefinition.durationUs,
|
||||||
periodOffsets[windowIndex],
|
periodOffsets[windowIndex],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue