use of com.google.android.exoplayer2.source.MediaSource.MediaPeriodId in project ExoPlayer by google.
the class ExoPlayerImplInternal method handleLoadingMediaPeriodChanged.
private void handleLoadingMediaPeriodChanged(boolean loadingTrackSelectionChanged) {
MediaPeriodHolder loadingMediaPeriodHolder = queue.getLoadingPeriod();
MediaPeriodId loadingMediaPeriodId = loadingMediaPeriodHolder == null ? playbackInfo.periodId : loadingMediaPeriodHolder.info.id;
boolean loadingMediaPeriodChanged = !playbackInfo.loadingMediaPeriodId.equals(loadingMediaPeriodId);
if (loadingMediaPeriodChanged) {
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(loadingMediaPeriodId);
}
playbackInfo.bufferedPositionUs = loadingMediaPeriodHolder == null ? playbackInfo.positionUs : loadingMediaPeriodHolder.getBufferedPositionUs();
playbackInfo.totalBufferedDurationUs = getTotalBufferedDurationUs();
if ((loadingMediaPeriodChanged || loadingTrackSelectionChanged) && loadingMediaPeriodHolder != null && loadingMediaPeriodHolder.prepared) {
updateLoadControlTrackSelection(loadingMediaPeriodHolder.getTrackGroups(), loadingMediaPeriodHolder.getTrackSelectorResult());
}
}
use of com.google.android.exoplayer2.source.MediaSource.MediaPeriodId in project ExoPlayer by google.
the class ExoPlayerImplInternal method handlePositionDiscontinuity.
@CheckResult
private PlaybackInfo handlePositionDiscontinuity(MediaPeriodId mediaPeriodId, long positionUs, long requestedContentPositionUs, long discontinuityStartPositionUs, boolean reportDiscontinuity, @DiscontinuityReason int discontinuityReason) {
deliverPendingMessageAtStartPositionRequired = deliverPendingMessageAtStartPositionRequired || positionUs != playbackInfo.positionUs || !mediaPeriodId.equals(playbackInfo.periodId);
resetPendingPauseAtEndOfPeriod();
TrackGroupArray trackGroupArray = playbackInfo.trackGroups;
TrackSelectorResult trackSelectorResult = playbackInfo.trackSelectorResult;
List<Metadata> staticMetadata = playbackInfo.staticMetadata;
if (mediaSourceList.isPrepared()) {
@Nullable MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
trackGroupArray = playingPeriodHolder == null ? TrackGroupArray.EMPTY : playingPeriodHolder.getTrackGroups();
trackSelectorResult = playingPeriodHolder == null ? emptyTrackSelectorResult : playingPeriodHolder.getTrackSelectorResult();
staticMetadata = extractMetadataFromTrackSelectionArray(trackSelectorResult.selections);
// Ensure the media period queue requested content position matches the new playback info.
if (playingPeriodHolder != null && playingPeriodHolder.info.requestedContentPositionUs != requestedContentPositionUs) {
playingPeriodHolder.info = playingPeriodHolder.info.copyWithRequestedContentPositionUs(requestedContentPositionUs);
}
} else if (!mediaPeriodId.equals(playbackInfo.periodId)) {
// Reset previously kept track info if unprepared and the period changes.
trackGroupArray = TrackGroupArray.EMPTY;
trackSelectorResult = emptyTrackSelectorResult;
staticMetadata = ImmutableList.of();
}
if (reportDiscontinuity) {
playbackInfoUpdate.setPositionDiscontinuity(discontinuityReason);
}
return playbackInfo.copyWithNewPosition(mediaPeriodId, positionUs, requestedContentPositionUs, discontinuityStartPositionUs, getTotalBufferedDurationUs(), trackGroupArray, trackSelectorResult, staticMetadata);
}
use of com.google.android.exoplayer2.source.MediaSource.MediaPeriodId in project ExoPlayer by google.
the class ExoPlayerImplInternal method resolvePositionForPlaylistChange.
private static PositionUpdateForPlaylistChange resolvePositionForPlaylistChange(Timeline timeline, PlaybackInfo playbackInfo, @Nullable SeekPosition pendingInitialSeekPosition, MediaPeriodQueue queue, @RepeatMode int repeatMode, boolean shuffleModeEnabled, Timeline.Window window, Timeline.Period period) {
if (timeline.isEmpty()) {
return new PositionUpdateForPlaylistChange(PlaybackInfo.getDummyPeriodForEmptyTimeline(), /* periodPositionUs= */
0, /* requestedContentPositionUs= */
C.TIME_UNSET, /* forceBufferingState= */
false, /* endPlayback= */
true, /* setTargetLiveOffset= */
false);
}
MediaPeriodId oldPeriodId = playbackInfo.periodId;
Object newPeriodUid = oldPeriodId.periodUid;
boolean isUsingPlaceholderPeriod = isUsingPlaceholderPeriod(playbackInfo, period);
long oldContentPositionUs = playbackInfo.periodId.isAd() || isUsingPlaceholderPeriod ? playbackInfo.requestedContentPositionUs : playbackInfo.positionUs;
long newContentPositionUs = oldContentPositionUs;
int startAtDefaultPositionWindowIndex = C.INDEX_UNSET;
boolean forceBufferingState = false;
boolean endPlayback = false;
boolean setTargetLiveOffset = false;
if (pendingInitialSeekPosition != null) {
// Resolve initial seek position.
@Nullable Pair<Object, Long> periodPosition = resolveSeekPositionUs(timeline, pendingInitialSeekPosition, /* trySubsequentPeriods= */
true, repeatMode, shuffleModeEnabled, window, period);
if (periodPosition == null) {
// The initial seek in the empty old timeline is invalid in the new timeline.
endPlayback = true;
startAtDefaultPositionWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
} else {
// The pending seek has been resolved successfully in the new timeline.
if (pendingInitialSeekPosition.windowPositionUs == C.TIME_UNSET) {
startAtDefaultPositionWindowIndex = timeline.getPeriodByUid(periodPosition.first, period).windowIndex;
} else {
newPeriodUid = periodPosition.first;
newContentPositionUs = periodPosition.second;
// Use explicit initial seek as new target live offset.
setTargetLiveOffset = true;
}
forceBufferingState = playbackInfo.playbackState == Player.STATE_ENDED;
}
} else if (playbackInfo.timeline.isEmpty()) {
// Resolve to default position if the old timeline is empty and no seek is requested above.
startAtDefaultPositionWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
} else if (timeline.getIndexOfPeriod(newPeriodUid) == C.INDEX_UNSET) {
// The current period isn't in the new timeline. Attempt to resolve a subsequent period whose
// window we can restart from.
@Nullable Object subsequentPeriodUid = resolveSubsequentPeriod(window, period, repeatMode, shuffleModeEnabled, newPeriodUid, playbackInfo.timeline, timeline);
if (subsequentPeriodUid == null) {
// We failed to resolve a suitable restart position but the timeline is not empty.
endPlayback = true;
startAtDefaultPositionWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
} else {
// We resolved a subsequent period. Start at the default position in the corresponding
// window.
startAtDefaultPositionWindowIndex = timeline.getPeriodByUid(subsequentPeriodUid, period).windowIndex;
}
} else if (oldContentPositionUs == C.TIME_UNSET) {
// The content was requested to start from its default position and we haven't used the
// resolved position yet. Re-resolve in case the default position changed.
startAtDefaultPositionWindowIndex = timeline.getPeriodByUid(newPeriodUid, period).windowIndex;
} else if (isUsingPlaceholderPeriod) {
// We previously requested a content position for a placeholder period, but haven't used it
// yet. Re-resolve the requested window position to the period position in case it changed.
playbackInfo.timeline.getPeriodByUid(oldPeriodId.periodUid, period);
if (playbackInfo.timeline.getWindow(period.windowIndex, window).firstPeriodIndex == playbackInfo.timeline.getIndexOfPeriod(oldPeriodId.periodUid)) {
// Only need to resolve the first period in a window because subsequent periods must start
// at position 0 and don't need to be resolved.
long windowPositionUs = oldContentPositionUs + period.getPositionInWindowUs();
int windowIndex = timeline.getPeriodByUid(newPeriodUid, period).windowIndex;
Pair<Object, Long> periodPositionUs = timeline.getPeriodPositionUs(window, period, windowIndex, windowPositionUs);
newPeriodUid = periodPositionUs.first;
newContentPositionUs = periodPositionUs.second;
}
// Use an explicitly requested content position as new target live offset.
setTargetLiveOffset = true;
}
// Set period uid for default positions and resolve position for ad resolution.
long contentPositionForAdResolutionUs = newContentPositionUs;
if (startAtDefaultPositionWindowIndex != C.INDEX_UNSET) {
Pair<Object, Long> defaultPositionUs = timeline.getPeriodPositionUs(window, period, startAtDefaultPositionWindowIndex, /* windowPositionUs= */
C.TIME_UNSET);
newPeriodUid = defaultPositionUs.first;
contentPositionForAdResolutionUs = defaultPositionUs.second;
newContentPositionUs = C.TIME_UNSET;
}
// Ensure ad insertion metadata is up to date.
MediaPeriodId periodIdWithAds = queue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange(timeline, newPeriodUid, contentPositionForAdResolutionUs);
boolean earliestCuePointIsUnchangedOrLater = periodIdWithAds.nextAdGroupIndex == C.INDEX_UNSET || (oldPeriodId.nextAdGroupIndex != C.INDEX_UNSET && periodIdWithAds.nextAdGroupIndex >= oldPeriodId.nextAdGroupIndex);
// Drop update if we keep playing the same content (MediaPeriod.periodUid are identical) and
// the only change is that MediaPeriodId.nextAdGroupIndex increased. This postpones a potential
// discontinuity until we reach the former next ad group position.
boolean sameOldAndNewPeriodUid = oldPeriodId.periodUid.equals(newPeriodUid);
boolean onlyNextAdGroupIndexIncreased = sameOldAndNewPeriodUid && !oldPeriodId.isAd() && !periodIdWithAds.isAd() && earliestCuePointIsUnchangedOrLater;
// Drop update if the change is from/to server-side inserted ads at the same content position to
// avoid any unintentional renderer reset.
boolean isInStreamAdChange = isIgnorableServerSideAdInsertionPeriodChange(isUsingPlaceholderPeriod, oldPeriodId, oldContentPositionUs, periodIdWithAds, timeline.getPeriodByUid(newPeriodUid, period), newContentPositionUs);
MediaPeriodId newPeriodId = onlyNextAdGroupIndexIncreased || isInStreamAdChange ? oldPeriodId : periodIdWithAds;
long periodPositionUs = contentPositionForAdResolutionUs;
if (newPeriodId.isAd()) {
if (newPeriodId.equals(oldPeriodId)) {
periodPositionUs = playbackInfo.positionUs;
} else {
timeline.getPeriodByUid(newPeriodId.periodUid, period);
periodPositionUs = newPeriodId.adIndexInAdGroup == period.getFirstAdIndexToPlay(newPeriodId.adGroupIndex) ? period.getAdResumePositionUs() : 0;
}
}
return new PositionUpdateForPlaylistChange(newPeriodId, periodPositionUs, newContentPositionUs, forceBufferingState, endPlayback, setTargetLiveOffset);
}
use of com.google.android.exoplayer2.source.MediaSource.MediaPeriodId in project ExoPlayer by google.
the class SsMediaPeriodTest method getSteamKeys_isCompatibleWithSsManifestFilter.
@Test
public void getSteamKeys_isCompatibleWithSsManifestFilter() {
SsManifest testManifest = createSsManifest(createStreamElement(/* name= */
"video", C.TRACK_TYPE_VIDEO, createVideoFormat(/* bitrate= */
200000), createVideoFormat(/* bitrate= */
400000), createVideoFormat(/* bitrate= */
800000)), createStreamElement(/* name= */
"audio", C.TRACK_TYPE_AUDIO, createAudioFormat(/* bitrate= */
48000), createAudioFormat(/* bitrate= */
96000)), createStreamElement(/* name= */
"text", C.TRACK_TYPE_TEXT, createTextFormat(/* language= */
"eng")));
FilterableManifestMediaPeriodFactory<SsManifest> mediaPeriodFactory = (manifest, periodIndex) -> {
MediaPeriodId mediaPeriodId = new MediaPeriodId(/* periodUid= */
new Object());
return new SsMediaPeriod(manifest, mock(SsChunkSource.Factory.class), mock(TransferListener.class), mock(CompositeSequenceableLoaderFactory.class), mock(DrmSessionManager.class), new DrmSessionEventListener.EventDispatcher().withParameters(/* windowIndex= */
0, mediaPeriodId), mock(LoadErrorHandlingPolicy.class), new MediaSourceEventListener.EventDispatcher().withParameters(/* windowIndex= */
0, mediaPeriodId, /* mediaTimeOffsetMs= */
0), mock(LoaderErrorThrower.class), mock(Allocator.class));
};
MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration(mediaPeriodFactory, testManifest);
}
use of com.google.android.exoplayer2.source.MediaSource.MediaPeriodId in project ExoPlayer by google.
the class SsMediaSource method createPeriod.
@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher = createEventDispatcher(id);
DrmSessionEventListener.EventDispatcher drmEventDispatcher = createDrmEventDispatcher(id);
SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, mediaTransferListener, compositeSequenceableLoaderFactory, drmSessionManager, drmEventDispatcher, loadErrorHandlingPolicy, mediaSourceEventDispatcher, manifestLoaderErrorThrower, allocator);
mediaPeriods.add(period);
return period;
}
Aggregations