Search in sources :

Example 51 with Renderer

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();
}
Also used : TextRenderer(com.google.android.exoplayer2.text.TextRenderer)

Example 52 with Renderer

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());
            }
        }
    }
}
Also used : TrackSelectorResult(com.google.android.exoplayer2.trackselection.TrackSelectorResult) TextRenderer(com.google.android.exoplayer2.text.TextRenderer) Nullable(androidx.annotation.Nullable) SampleStream(com.google.android.exoplayer2.source.SampleStream)

Example 53 with Renderer

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();
    }
}
Also used : TextRenderer(com.google.android.exoplayer2.text.TextRenderer) MediaPeriodId(com.google.android.exoplayer2.source.MediaSource.MediaPeriodId)

Example 54 with Renderer

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;
}
Also used : TextRenderer(com.google.android.exoplayer2.text.TextRenderer) SampleStream(com.google.android.exoplayer2.source.SampleStream)

Example 55 with Renderer

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);
}
Also used : MediaPeriodId(com.google.android.exoplayer2.source.MediaSource.MediaPeriodId) Nullable(androidx.annotation.Nullable)

Aggregations

Test (org.junit.Test)36 FakeRenderer (com.google.android.exoplayer2.testutil.FakeRenderer)27 Nullable (androidx.annotation.Nullable)25 FakeMediaSource (com.google.android.exoplayer2.testutil.FakeMediaSource)25 FakeTimeline (com.google.android.exoplayer2.testutil.FakeTimeline)25 TestExoPlayerBuilder (com.google.android.exoplayer2.testutil.TestExoPlayerBuilder)24 TextRenderer (com.google.android.exoplayer2.text.TextRenderer)21 TrackGroupArray (com.google.android.exoplayer2.source.TrackGroupArray)19 ArrayList (java.util.ArrayList)18 SinglePeriodTimeline (com.google.android.exoplayer2.source.SinglePeriodTimeline)17 NoUidTimeline (com.google.android.exoplayer2.testutil.NoUidTimeline)17 MediaSource (com.google.android.exoplayer2.source.MediaSource)16 TrackGroup (com.google.android.exoplayer2.source.TrackGroup)16 ActionSchedule (com.google.android.exoplayer2.testutil.ActionSchedule)16 Format (com.google.android.exoplayer2.Format)15 Listener (com.google.android.exoplayer2.Player.Listener)15 ExoPlayerTestRunner (com.google.android.exoplayer2.testutil.ExoPlayerTestRunner)15 TimelineWindowDefinition (com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition)14 InOrder (org.mockito.InOrder)14 ConcatenatingMediaSource (com.google.android.exoplayer2.source.ConcatenatingMediaSource)13