use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class ExoPlayerImplInternal method resetRendererPosition.
private void resetRendererPosition(long periodPositionUs) throws ExoPlaybackException {
MediaPeriodHolder playingMediaPeriod = queue.getPlayingPeriod();
rendererPositionUs = playingMediaPeriod == null ? MediaPeriodQueue.INITIAL_RENDERER_POSITION_OFFSET_US + periodPositionUs : playingMediaPeriod.toRendererTime(periodPositionUs);
mediaClock.resetPosition(rendererPositionUs);
for (Renderer renderer : renderers) {
if (isRendererEnabled(renderer)) {
renderer.resetPosition(rendererPositionUs);
}
}
notifyTrackSelectionDiscontinuity();
}
use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class ExoPlayerImplInternal method maybeUpdateReadingPeriod.
private void maybeUpdateReadingPeriod() {
@Nullable MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
if (readingPeriodHolder == null) {
return;
}
if (readingPeriodHolder.getNext() == null || pendingPauseAtEndOfPeriod) {
// intentionally to pause at the end of the period.
if (readingPeriodHolder.info.isFinal || pendingPauseAtEndOfPeriod) {
for (int i = 0; i < renderers.length; i++) {
Renderer renderer = renderers[i];
SampleStream sampleStream = readingPeriodHolder.sampleStreams[i];
// stream in case of playlist changes that cause the stream to be no longer final.
if (sampleStream != null && renderer.getStream() == sampleStream && renderer.hasReadStreamToEnd()) {
long streamEndPositionUs = readingPeriodHolder.info.durationUs != C.TIME_UNSET && readingPeriodHolder.info.durationUs != C.TIME_END_OF_SOURCE ? readingPeriodHolder.getRendererOffset() + readingPeriodHolder.info.durationUs : C.TIME_UNSET;
setCurrentStreamFinal(renderer, streamEndPositionUs);
}
}
}
return;
}
if (!hasReadingPeriodFinishedReading()) {
return;
}
if (!readingPeriodHolder.getNext().prepared && rendererPositionUs < readingPeriodHolder.getNext().getStartPositionRendererTime()) {
// The successor is not prepared yet and playback hasn't reached the transition point.
return;
}
MediaPeriodHolder oldReadingPeriodHolder = readingPeriodHolder;
TrackSelectorResult oldTrackSelectorResult = readingPeriodHolder.getTrackSelectorResult();
readingPeriodHolder = queue.advanceReadingPeriod();
TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.getTrackSelectorResult();
updatePlaybackSpeedSettingsForNewPeriod(/* newTimeline= */
playbackInfo.timeline, /* newPeriodId= */
readingPeriodHolder.info.id, /* oldTimeline= */
playbackInfo.timeline, /* oldPeriodId= */
oldReadingPeriodHolder.info.id, /* positionForTargetOffsetOverrideUs= */
C.TIME_UNSET);
if (readingPeriodHolder.prepared && readingPeriodHolder.mediaPeriod.readDiscontinuity() != C.TIME_UNSET) {
// The new period starts with a discontinuity, so the renderers will play out all data, then
// be disabled and re-enabled when they start playing the next period.
setAllRendererStreamsFinal(/* streamEndPositionUs= */
readingPeriodHolder.getStartPositionRendererTime());
return;
}
for (int i = 0; i < renderers.length; i++) {
boolean oldRendererEnabled = oldTrackSelectorResult.isRendererEnabled(i);
boolean newRendererEnabled = newTrackSelectorResult.isRendererEnabled(i);
if (oldRendererEnabled && !renderers[i].isCurrentStreamFinal()) {
boolean isNoSampleRenderer = rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE;
RendererConfiguration oldConfig = oldTrackSelectorResult.rendererConfigurations[i];
RendererConfiguration newConfig = newTrackSelectorResult.rendererConfigurations[i];
if (!newRendererEnabled || !newConfig.equals(oldConfig) || isNoSampleRenderer) {
// The renderer will be disabled when transitioning to playing the next period, because
// there's no new selection, or because a configuration change is required, or because
// it's a no-sample renderer for which rendererOffsetUs should be updated only when
// starting to play the next period. Mark the SampleStream as final to play out any
// remaining data.
setCurrentStreamFinal(renderers[i], /* streamEndPositionUs= */
readingPeriodHolder.getStartPositionRendererTime());
}
}
}
}
use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class ExoPlayerImplInternal method resetInternal.
private void resetInternal(boolean resetRenderers, boolean resetPosition, boolean releaseMediaSourceList, boolean resetError) {
handler.removeMessages(MSG_DO_SOME_WORK);
pendingRecoverableRendererError = null;
isRebuffering = false;
mediaClock.stop();
rendererPositionUs = MediaPeriodQueue.INITIAL_RENDERER_POSITION_OFFSET_US;
for (Renderer renderer : renderers) {
try {
disableRenderer(renderer);
} catch (ExoPlaybackException | RuntimeException e) {
// There's nothing we can do.
Log.e(TAG, "Disable failed.", e);
}
}
if (resetRenderers) {
for (Renderer renderer : renderers) {
if (renderersToReset.remove(renderer)) {
try {
renderer.reset();
} catch (RuntimeException e) {
// There's nothing we can do.
Log.e(TAG, "Reset failed.", e);
}
}
}
}
enabledRendererCount = 0;
MediaPeriodId mediaPeriodId = playbackInfo.periodId;
long startPositionUs = playbackInfo.positionUs;
long requestedContentPositionUs = playbackInfo.periodId.isAd() || isUsingPlaceholderPeriod(playbackInfo, period) ? playbackInfo.requestedContentPositionUs : playbackInfo.positionUs;
boolean resetTrackInfo = false;
if (resetPosition) {
pendingInitialSeekPosition = null;
Pair<MediaPeriodId, Long> firstPeriodAndPositionUs = getPlaceholderFirstMediaPeriodPositionUs(playbackInfo.timeline);
mediaPeriodId = firstPeriodAndPositionUs.first;
startPositionUs = firstPeriodAndPositionUs.second;
requestedContentPositionUs = C.TIME_UNSET;
if (!mediaPeriodId.equals(playbackInfo.periodId)) {
resetTrackInfo = true;
}
}
queue.clear();
shouldContinueLoading = false;
playbackInfo = new PlaybackInfo(playbackInfo.timeline, mediaPeriodId, requestedContentPositionUs, /* discontinuityStartPositionUs= */
startPositionUs, playbackInfo.playbackState, resetError ? null : playbackInfo.playbackError, /* isLoading= */
false, resetTrackInfo ? TrackGroupArray.EMPTY : playbackInfo.trackGroups, resetTrackInfo ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult, resetTrackInfo ? ImmutableList.of() : playbackInfo.staticMetadata, mediaPeriodId, playbackInfo.playWhenReady, playbackInfo.playbackSuppressionReason, playbackInfo.playbackParameters, /* bufferedPositionUs= */
startPositionUs, /* totalBufferedDurationUs= */
0, /* positionUs= */
startPositionUs, offloadSchedulingEnabled, /* sleepingForOffload= */
false);
if (releaseMediaSourceList) {
mediaSourceList.release();
}
}
use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class ExoPlayerImplInternal method hasReadingPeriodFinishedReading.
private boolean hasReadingPeriodFinishedReading() {
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
if (!readingPeriodHolder.prepared) {
return false;
}
for (int i = 0; i < renderers.length; i++) {
Renderer renderer = renderers[i];
SampleStream sampleStream = readingPeriodHolder.sampleStreams[i];
if (renderer.getStream() != sampleStream || (sampleStream != null && !renderer.hasReadStreamToEnd() && !hasReachedServerSideInsertedAdsTransition(renderer, readingPeriodHolder))) {
// The current reading period is still being read by at least one renderer.
return false;
}
}
return true;
}
use of com.google.android.exoplayer2.Renderer 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);
}
Aggregations