Add start position to MediaSource.createPeriod.
That's the same position set in MediaPeriod.prepare (where it may be removed in the future). Having the position at an earlier point is necessary to fix an issue with lazy preparation in ConcatenatingMediaSource where the prepare position was assumed to be known but MediaPeriod.prepare hasn't been called yet. Issue:#5350 PiperOrigin-RevId: 229756637
This commit is contained in:
parent
76baa5724c
commit
22413b8037
@ -39,6 +39,10 @@
|
||||
* Remove `player` and `isTopLevelSource` parameters from `MediaSource.prepare`.
|
||||
* Change signature of `PlayerNotificationManager.NotificationListener` to better
|
||||
fit service requirements. Remove ability to set a custom stop action.
|
||||
* Add `startPositionUs` to `MediaSource.createPeriod`. This fixes an issue where
|
||||
using lazy preparation in `ConcatenatingMediaSource` with an
|
||||
`ExtractorMediaSource` overrides initial seek positions
|
||||
([#5350](https://github.com/google/ExoPlayer/issues/5350)).
|
||||
|
||||
### 2.9.4 ###
|
||||
|
||||
|
@ -93,8 +93,8 @@ public final class ImaAdsMediaSource extends BaseMediaSource implements SourceIn
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
return adsMediaSource.createPeriod(id, allocator);
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
return adsMediaSource.createPeriod(id, allocator, startPositionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -89,7 +89,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
this.info = info;
|
||||
sampleStreams = new SampleStream[rendererCapabilities.length];
|
||||
mayRetainStreamFlags = new boolean[rendererCapabilities.length];
|
||||
mediaPeriod = createMediaPeriod(info.id, mediaSource, allocator);
|
||||
mediaPeriod = createMediaPeriod(info.id, mediaSource, allocator, info.startPositionUs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -399,8 +399,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
|
||||
/** Returns a media period corresponding to the given {@code id}. */
|
||||
private static MediaPeriod createMediaPeriod(
|
||||
MediaPeriodId id, MediaSource mediaSource, Allocator allocator) {
|
||||
MediaPeriod mediaPeriod = mediaSource.createPeriod(id, allocator);
|
||||
MediaPeriodId id, MediaSource mediaSource, Allocator allocator, long startPositionUs) {
|
||||
MediaPeriod mediaPeriod = mediaSource.createPeriod(id, allocator, startPositionUs);
|
||||
if (id.endPositionUs != C.TIME_UNSET && id.endPositionUs != C.TIME_END_OF_SOURCE) {
|
||||
mediaPeriod =
|
||||
new ClippingMediaPeriod(
|
||||
|
@ -206,10 +206,10 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
ClippingMediaPeriod mediaPeriod =
|
||||
new ClippingMediaPeriod(
|
||||
mediaSource.createPeriod(id, allocator),
|
||||
mediaSource.createPeriod(id, allocator, startPositionUs),
|
||||
enableInitialDiscontinuity,
|
||||
periodStartUs,
|
||||
periodEndUs);
|
||||
|
@ -438,7 +438,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public final MediaPeriod createPeriod(
|
||||
MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
Object mediaSourceHolderUid = getMediaSourceHolderUid(id.periodUid);
|
||||
MediaSourceHolder holder = mediaSourceByUid.get(mediaSourceHolderUid);
|
||||
if (holder == null) {
|
||||
@ -446,7 +447,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
||||
holder = new MediaSourceHolder(new DummyMediaSource());
|
||||
holder.hasStartedPreparing = true;
|
||||
}
|
||||
DeferredMediaPeriod mediaPeriod = new DeferredMediaPeriod(holder.mediaSource, id, allocator);
|
||||
DeferredMediaPeriod mediaPeriod =
|
||||
new DeferredMediaPeriod(holder.mediaSource, id, allocator, startPositionUs);
|
||||
mediaSourceByMediaPeriod.put(mediaPeriod, holder);
|
||||
holder.activeMediaPeriods.add(mediaPeriod);
|
||||
if (!holder.hasStartedPreparing) {
|
||||
@ -1144,7 +1146,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Media period that wraps a media source and defers calling its {@link
|
||||
* MediaSource#createPeriod(MediaPeriodId, Allocator)} method until {@link
|
||||
* MediaSource#createPeriod(MediaPeriodId, Allocator, long)} method until {@link
|
||||
* #createPeriod(MediaPeriodId)} has been called. This is useful if you need to return a media
|
||||
* period immediately but the media source that should create it is not yet prepared.
|
||||
*/
|
||||
@ -60,11 +60,14 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
|
||||
* @param mediaSource The media source to wrap.
|
||||
* @param id The identifier used to create the deferred media period.
|
||||
* @param allocator The allocator used to create the media period.
|
||||
* @param preparePositionUs The expected start position, in microseconds.
|
||||
*/
|
||||
public DeferredMediaPeriod(MediaSource mediaSource, MediaPeriodId id, Allocator allocator) {
|
||||
public DeferredMediaPeriod(
|
||||
MediaSource mediaSource, MediaPeriodId id, Allocator allocator, long preparePositionUs) {
|
||||
this.id = id;
|
||||
this.allocator = allocator;
|
||||
this.mediaSource = mediaSource;
|
||||
this.preparePositionUs = preparePositionUs;
|
||||
preparePositionOverrideUs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@ -86,28 +89,25 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
|
||||
|
||||
/**
|
||||
* Overrides the default prepare position at which to prepare the media period. This value is only
|
||||
* used if the call to {@link MediaPeriod#prepare(Callback, long)} is being deferred.
|
||||
* used if called before {@link #createPeriod(MediaPeriodId)}.
|
||||
*
|
||||
* @param defaultPreparePositionUs The default prepare position to use, in microseconds.
|
||||
* @param preparePositionUs The default prepare position to use, in microseconds.
|
||||
*/
|
||||
public void overridePreparePositionUs(long defaultPreparePositionUs) {
|
||||
preparePositionOverrideUs = defaultPreparePositionUs;
|
||||
public void overridePreparePositionUs(long preparePositionUs) {
|
||||
preparePositionOverrideUs = preparePositionUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator)} on the wrapped source then
|
||||
* prepares it if {@link #prepare(Callback, long)} has been called. Call {@link #releasePeriod()}
|
||||
* to release the period.
|
||||
* Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator, long)} on the wrapped source
|
||||
* then prepares it if {@link #prepare(Callback, long)} has been called. Call {@link
|
||||
* #releasePeriod()} to release the period.
|
||||
*
|
||||
* @param id The identifier that should be used to create the media period from the media source.
|
||||
*/
|
||||
public void createPeriod(MediaPeriodId id) {
|
||||
mediaPeriod = mediaSource.createPeriod(id, allocator);
|
||||
long preparePositionUs = getPreparePositionWithOverride(this.preparePositionUs);
|
||||
mediaPeriod = mediaSource.createPeriod(id, allocator, preparePositionUs);
|
||||
if (callback != null) {
|
||||
long preparePositionUs =
|
||||
preparePositionOverrideUs != C.TIME_UNSET
|
||||
? preparePositionOverrideUs
|
||||
: this.preparePositionUs;
|
||||
mediaPeriod.prepare(this, preparePositionUs);
|
||||
}
|
||||
}
|
||||
@ -124,9 +124,8 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
|
||||
@Override
|
||||
public void prepare(Callback callback, long preparePositionUs) {
|
||||
this.callback = callback;
|
||||
this.preparePositionUs = preparePositionUs;
|
||||
if (mediaPeriod != null) {
|
||||
mediaPeriod.prepare(this, preparePositionUs);
|
||||
mediaPeriod.prepare(this, getPreparePositionWithOverride(this.preparePositionUs));
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,4 +216,9 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
|
||||
callback.onPrepared(this);
|
||||
}
|
||||
|
||||
private long getPreparePositionWithOverride(long preparePositionUs) {
|
||||
return preparePositionOverrideUs != C.TIME_UNSET
|
||||
? preparePositionOverrideUs
|
||||
: preparePositionUs;
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
DataSource dataSource = dataSourceFactory.createDataSource();
|
||||
if (transferListener != null) {
|
||||
dataSource.addTransferListener(transferListener);
|
||||
|
@ -77,14 +77,15 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
if (loopCount == Integer.MAX_VALUE) {
|
||||
return childSource.createPeriod(id, allocator);
|
||||
return childSource.createPeriod(id, allocator, startPositionUs);
|
||||
}
|
||||
Object childPeriodUid = LoopingTimeline.getChildPeriodUidFromConcatenatedUid(id.periodUid);
|
||||
MediaPeriodId childMediaPeriodId = id.copyWithPeriodUid(childPeriodUid);
|
||||
childMediaPeriodIdToMediaPeriodId.put(childMediaPeriodId, id);
|
||||
MediaPeriod mediaPeriod = childSource.createPeriod(childMediaPeriodId, allocator);
|
||||
MediaPeriod mediaPeriod =
|
||||
childSource.createPeriod(childMediaPeriodId, allocator, startPositionUs);
|
||||
mediaPeriodToChildMediaPeriodId.put(mediaPeriod, childMediaPeriodId);
|
||||
return mediaPeriod;
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ import java.io.IOException;
|
||||
* on the {@link SourceInfoRefreshListener}s passed to {@link
|
||||
* #prepareSource(SourceInfoRefreshListener, TransferListener)}.
|
||||
* <li>To provide {@link MediaPeriod} instances for the periods in its timeline. MediaPeriods are
|
||||
* obtained by calling {@link #createPeriod(MediaPeriodId, Allocator)}, and provide a way for
|
||||
* the player to load and read the media.
|
||||
* obtained by calling {@link #createPeriod(MediaPeriodId, Allocator, long)}, and provide a
|
||||
* way for the player to load and read the media.
|
||||
* </ul>
|
||||
*
|
||||
* All methods are called on the player's internal playback thread, as described in the {@link
|
||||
@ -261,9 +261,10 @@ public interface MediaSource {
|
||||
*
|
||||
* @param id The identifier of the period.
|
||||
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
|
||||
* @param startPositionUs The expected start position, in microseconds.
|
||||
* @return A new {@link MediaPeriod}.
|
||||
*/
|
||||
MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator);
|
||||
MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs);
|
||||
|
||||
/**
|
||||
* Releases the period.
|
||||
|
@ -120,13 +120,13 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
MediaPeriod[] periods = new MediaPeriod[mediaSources.length];
|
||||
int periodIndex = timelines[0].getIndexOfPeriod(id.periodUid);
|
||||
for (int i = 0; i < periods.length; i++) {
|
||||
MediaPeriodId childMediaPeriodId =
|
||||
id.copyWithPeriodUid(timelines[i].getUidOfPeriod(periodIndex));
|
||||
periods[i] = mediaSources[i].createPeriod(childMediaPeriodId, allocator);
|
||||
periods[i] = mediaSources[i].createPeriod(childMediaPeriodId, allocator, startPositionUs);
|
||||
}
|
||||
return new MergingMediaPeriod(compositeSequenceableLoaderFactory, periods);
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
return new SingleSampleMediaPeriod(
|
||||
dataSpec,
|
||||
dataSourceFactory,
|
||||
|
@ -334,7 +334,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
if (adPlaybackState.adGroupCount > 0 && id.isAd()) {
|
||||
int adGroupIndex = id.adGroupIndex;
|
||||
int adIndexInAdGroup = id.adIndexInAdGroup;
|
||||
@ -353,7 +353,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
|
||||
prepareChildSource(id, adMediaSource);
|
||||
}
|
||||
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
|
||||
DeferredMediaPeriod deferredMediaPeriod = new DeferredMediaPeriod(mediaSource, id, allocator);
|
||||
DeferredMediaPeriod deferredMediaPeriod =
|
||||
new DeferredMediaPeriod(mediaSource, id, allocator, startPositionUs);
|
||||
deferredMediaPeriod.setPrepareErrorListener(
|
||||
new AdPrepareErrorListener(adUri, adGroupIndex, adIndexInAdGroup));
|
||||
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource);
|
||||
@ -369,7 +370,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
|
||||
}
|
||||
return deferredMediaPeriod;
|
||||
} else {
|
||||
DeferredMediaPeriod mediaPeriod = new DeferredMediaPeriod(contentMediaSource, id, allocator);
|
||||
DeferredMediaPeriod mediaPeriod =
|
||||
new DeferredMediaPeriod(contentMediaSource, id, allocator, startPositionUs);
|
||||
mediaPeriod.createPeriod(id);
|
||||
return mediaPeriod;
|
||||
}
|
||||
|
@ -654,7 +654,8 @@ public final class DashMediaSource extends BaseMediaSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId periodId, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(
|
||||
MediaPeriodId periodId, Allocator allocator, long startPositionUs) {
|
||||
int periodIndex = (Integer) periodId.periodUid - firstPeriodId;
|
||||
EventDispatcher periodEventDispatcher =
|
||||
createEventDispatcher(periodId, manifest.getPeriod(periodIndex).startMs);
|
||||
|
@ -428,7 +428,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
EventDispatcher eventDispatcher = createEventDispatcher(id);
|
||||
return new HlsMediaPeriod(
|
||||
extractorFactory,
|
||||
|
@ -552,7 +552,7 @@ public final class SsMediaSource extends BaseMediaSource
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
EventDispatcher eventDispatcher = createEventDispatcher(id);
|
||||
SsMediaPeriod period =
|
||||
new SsMediaPeriod(
|
||||
|
@ -112,7 +112,7 @@ public class FakeMediaSource extends BaseMediaSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
assertThat(preparedSource).isTrue();
|
||||
assertThat(releasedSource).isFalse();
|
||||
int periodIndex = timeline.getIndexOfPeriod(id.periodUid);
|
||||
|
@ -132,15 +132,28 @@ public class MediaSourceTestRunner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MediaSource#createPeriod(MediaSource.MediaPeriodId, Allocator)} on the playback
|
||||
* thread, asserting that a non-null {@link MediaPeriod} is returned.
|
||||
* Calls {@link MediaSource#createPeriod(MediaSource.MediaPeriodId, Allocator, long)} with a zero
|
||||
* start position on the playback thread, asserting that a non-null {@link MediaPeriod} is
|
||||
* returned.
|
||||
*
|
||||
* @param periodId The id of the period to create.
|
||||
* @return The created {@link MediaPeriod}.
|
||||
*/
|
||||
public MediaPeriod createPeriod(final MediaPeriodId periodId) {
|
||||
return createPeriod(periodId, /* startPositionUs= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MediaSource#createPeriod(MediaSource.MediaPeriodId, Allocator, long)} on the
|
||||
* playback thread, asserting that a non-null {@link MediaPeriod} is returned.
|
||||
*
|
||||
* @param periodId The id of the period to create.
|
||||
* @return The created {@link MediaPeriod}.
|
||||
*/
|
||||
public MediaPeriod createPeriod(final MediaPeriodId periodId, long startPositionUs) {
|
||||
final MediaPeriod[] holder = new MediaPeriod[1];
|
||||
runOnPlaybackThread(() -> holder[0] = mediaSource.createPeriod(periodId, allocator));
|
||||
runOnPlaybackThread(
|
||||
() -> holder[0] = mediaSource.createPeriod(periodId, allocator, startPositionUs));
|
||||
assertThat(holder[0]).isNotNull();
|
||||
return holder[0];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user