Search in sources :

Example 11 with Renderer

use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.

the class ExoPlayerImplInternal method enableRenderer.

private void enableRenderer(int rendererIndex, boolean wasRendererEnabled) throws ExoPlaybackException {
    Renderer renderer = renderers[rendererIndex];
    if (isRendererEnabled(renderer)) {
        return;
    }
    MediaPeriodHolder periodHolder = queue.getReadingPeriod();
    boolean mayRenderStartOfStream = periodHolder == queue.getPlayingPeriod();
    TrackSelectorResult trackSelectorResult = periodHolder.getTrackSelectorResult();
    RendererConfiguration rendererConfiguration = trackSelectorResult.rendererConfigurations[rendererIndex];
    ExoTrackSelection newSelection = trackSelectorResult.selections[rendererIndex];
    Format[] formats = getFormats(newSelection);
    // The renderer needs enabling with its new track selection.
    boolean playing = shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY;
    // Consider as joining only if the renderer was previously disabled.
    boolean joining = !wasRendererEnabled && playing;
    // Enable the renderer.
    enabledRendererCount++;
    renderersToReset.add(renderer);
    renderer.enable(rendererConfiguration, formats, periodHolder.sampleStreams[rendererIndex], rendererPositionUs, joining, mayRenderStartOfStream, periodHolder.getStartPositionRendererTime(), periodHolder.getRendererOffset());
    renderer.handleMessage(Renderer.MSG_SET_WAKEUP_LISTENER, new Renderer.WakeupListener() {

        @Override
        public void onSleep(long wakeupDeadlineMs) {
            // Do not sleep if the expected sleep time is not long enough to save significant power.
            if (wakeupDeadlineMs >= MIN_RENDERER_SLEEP_DURATION_MS) {
                requestForRendererSleep = true;
            }
        }

        @Override
        public void onWakeup() {
            handler.sendEmptyMessage(MSG_DO_SOME_WORK);
        }
    });
    mediaClock.onRendererEnabled(renderer);
    // Start the renderer if playing.
    if (playing) {
        renderer.start();
    }
}
Also used : TrackSelectorResult(com.google.android.exoplayer2.trackselection.TrackSelectorResult) ExoTrackSelection(com.google.android.exoplayer2.trackselection.ExoTrackSelection) TextRenderer(com.google.android.exoplayer2.text.TextRenderer)

Example 12 with Renderer

use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.

the class ExoPlayerImplInternal method doSomeWork.

private void doSomeWork() throws ExoPlaybackException, IOException {
    long operationStartTimeMs = clock.uptimeMillis();
    updatePeriods();
    if (playbackInfo.playbackState == Player.STATE_IDLE || playbackInfo.playbackState == Player.STATE_ENDED) {
        // Remove all messages. Prepare (in case of IDLE) or seek (in case of ENDED) will resume.
        handler.removeMessages(MSG_DO_SOME_WORK);
        return;
    }
    @Nullable MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
    if (playingPeriodHolder == null) {
        // We're still waiting until the playing period is available.
        scheduleNextWork(operationStartTimeMs, ACTIVE_INTERVAL_MS);
        return;
    }
    TraceUtil.beginSection("doSomeWork");
    updatePlaybackPositions();
    boolean renderersEnded = true;
    boolean renderersAllowPlayback = true;
    if (playingPeriodHolder.prepared) {
        long rendererPositionElapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
        playingPeriodHolder.mediaPeriod.discardBuffer(playbackInfo.positionUs - backBufferDurationUs, retainBackBufferFromKeyframe);
        for (int i = 0; i < renderers.length; i++) {
            Renderer renderer = renderers[i];
            if (!isRendererEnabled(renderer)) {
                continue;
            }
            // TODO: Each renderer should return the maximum delay before which it wishes to be called
            // again. The minimum of these values should then be used as the delay before the next
            // invocation of this method.
            renderer.render(rendererPositionUs, rendererPositionElapsedRealtimeUs);
            renderersEnded = renderersEnded && renderer.isEnded();
            // Determine whether the renderer allows playback to continue. Playback can continue if the
            // renderer is ready or ended. Also continue playback if the renderer is reading ahead into
            // the next stream or is waiting for the next stream. This is to avoid getting stuck if
            // tracks in the current period have uneven durations and are still being read by another
            // renderer. See: https://github.com/google/ExoPlayer/issues/1874.
            boolean isReadingAhead = playingPeriodHolder.sampleStreams[i] != renderer.getStream();
            boolean isWaitingForNextStream = !isReadingAhead && renderer.hasReadStreamToEnd();
            boolean allowsPlayback = isReadingAhead || isWaitingForNextStream || renderer.isReady() || renderer.isEnded();
            renderersAllowPlayback = renderersAllowPlayback && allowsPlayback;
            if (!allowsPlayback) {
                renderer.maybeThrowStreamError();
            }
        }
    } else {
        playingPeriodHolder.mediaPeriod.maybeThrowPrepareError();
    }
    long playingPeriodDurationUs = playingPeriodHolder.info.durationUs;
    boolean finishedRendering = renderersEnded && playingPeriodHolder.prepared && (playingPeriodDurationUs == C.TIME_UNSET || playingPeriodDurationUs <= playbackInfo.positionUs);
    if (finishedRendering && pendingPauseAtEndOfPeriod) {
        pendingPauseAtEndOfPeriod = false;
        setPlayWhenReadyInternal(/* playWhenReady= */
        false, playbackInfo.playbackSuppressionReason, /* operationAck= */
        false, Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM);
    }
    if (finishedRendering && playingPeriodHolder.info.isFinal) {
        setState(Player.STATE_ENDED);
        stopRenderers();
    } else if (playbackInfo.playbackState == Player.STATE_BUFFERING && shouldTransitionToReadyState(renderersAllowPlayback)) {
        setState(Player.STATE_READY);
        // Any pending error was successfully recovered from.
        pendingRecoverableRendererError = null;
        if (shouldPlayWhenReady()) {
            startRenderers();
        }
    } else if (playbackInfo.playbackState == Player.STATE_READY && !(enabledRendererCount == 0 ? isTimelineReady() : renderersAllowPlayback)) {
        isRebuffering = shouldPlayWhenReady();
        setState(Player.STATE_BUFFERING);
        if (isRebuffering) {
            notifyTrackSelectionRebuffer();
            livePlaybackSpeedControl.notifyRebuffer();
        }
        stopRenderers();
    }
    boolean playbackMaybeStuck = false;
    if (playbackInfo.playbackState == Player.STATE_BUFFERING) {
        for (int i = 0; i < renderers.length; i++) {
            if (isRendererEnabled(renderers[i]) && renderers[i].getStream() == playingPeriodHolder.sampleStreams[i]) {
                renderers[i].maybeThrowStreamError();
            }
        }
        if (!playbackInfo.isLoading && playbackInfo.totalBufferedDurationUs < 500_000 && isLoadingPossible()) {
            // The renderers are not ready, there is more media available to load, and the LoadControl
            // is refusing to load it (indicated by !playbackInfo.isLoading). This could be because the
            // renderers are still transitioning to their ready states, but it could also indicate a
            // stuck playback. The playbackInfo.totalBufferedDurationUs check further isolates the
            // cause to a lack of media for the renderers to consume, to avoid classifying playbacks as
            // stuck when they're waiting for other reasons (in particular, loading DRM keys).
            playbackMaybeStuck = true;
        }
    }
    if (!playbackMaybeStuck) {
        playbackMaybeBecameStuckAtMs = C.TIME_UNSET;
    } else if (playbackMaybeBecameStuckAtMs == C.TIME_UNSET) {
        playbackMaybeBecameStuckAtMs = clock.elapsedRealtime();
    } else if (clock.elapsedRealtime() - playbackMaybeBecameStuckAtMs >= PLAYBACK_STUCK_AFTER_MS) {
        throw new IllegalStateException("Playback stuck buffering and not loading");
    }
    if (offloadSchedulingEnabled != playbackInfo.offloadSchedulingEnabled) {
        playbackInfo = playbackInfo.copyWithOffloadSchedulingEnabled(offloadSchedulingEnabled);
    }
    boolean sleepingForOffload = false;
    if ((shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY) || playbackInfo.playbackState == Player.STATE_BUFFERING) {
        sleepingForOffload = !maybeScheduleWakeup(operationStartTimeMs, ACTIVE_INTERVAL_MS);
    } else if (enabledRendererCount != 0 && playbackInfo.playbackState != Player.STATE_ENDED) {
        scheduleNextWork(operationStartTimeMs, IDLE_INTERVAL_MS);
    } else {
        handler.removeMessages(MSG_DO_SOME_WORK);
    }
    if (playbackInfo.sleepingForOffload != sleepingForOffload) {
        playbackInfo = playbackInfo.copyWithSleepingForOffload(sleepingForOffload);
    }
    // A sleep request is only valid for the current doSomeWork.
    requestForRendererSleep = false;
    TraceUtil.endSection();
}
Also used : TextRenderer(com.google.android.exoplayer2.text.TextRenderer) Nullable(androidx.annotation.Nullable)

Example 13 with Renderer

use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.

the class ExoPlayerImplInternal method reselectTracksInternal.

private void reselectTracksInternal() throws ExoPlaybackException {
    float playbackSpeed = mediaClock.getPlaybackParameters().speed;
    // Reselect tracks on each period in turn, until the selection changes.
    MediaPeriodHolder periodHolder = queue.getPlayingPeriod();
    MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
    boolean selectionsChangedForReadPeriod = true;
    TrackSelectorResult newTrackSelectorResult;
    while (true) {
        if (periodHolder == null || !periodHolder.prepared) {
            // The reselection did not change any prepared periods.
            return;
        }
        newTrackSelectorResult = periodHolder.selectTracks(playbackSpeed, playbackInfo.timeline);
        if (!newTrackSelectorResult.isEquivalent(periodHolder.getTrackSelectorResult())) {
            // Selected tracks have changed for this period.
            break;
        }
        if (periodHolder == readingPeriodHolder) {
            // The track reselection didn't affect any period that has been read.
            selectionsChangedForReadPeriod = false;
        }
        periodHolder = periodHolder.getNext();
    }
    if (selectionsChangedForReadPeriod) {
        // Update streams and rebuffer for the new selection, recreating all streams if reading ahead.
        MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
        boolean recreateStreams = queue.removeAfter(playingPeriodHolder);
        boolean[] streamResetFlags = new boolean[renderers.length];
        long periodPositionUs = playingPeriodHolder.applyTrackSelection(newTrackSelectorResult, playbackInfo.positionUs, recreateStreams, streamResetFlags);
        boolean hasDiscontinuity = playbackInfo.playbackState != Player.STATE_ENDED && periodPositionUs != playbackInfo.positionUs;
        playbackInfo = handlePositionDiscontinuity(playbackInfo.periodId, periodPositionUs, playbackInfo.requestedContentPositionUs, playbackInfo.discontinuityStartPositionUs, hasDiscontinuity, Player.DISCONTINUITY_REASON_INTERNAL);
        if (hasDiscontinuity) {
            resetRendererPosition(periodPositionUs);
        }
        boolean[] rendererWasEnabledFlags = new boolean[renderers.length];
        for (int i = 0; i < renderers.length; i++) {
            Renderer renderer = renderers[i];
            rendererWasEnabledFlags[i] = isRendererEnabled(renderer);
            SampleStream sampleStream = playingPeriodHolder.sampleStreams[i];
            if (rendererWasEnabledFlags[i]) {
                if (sampleStream != renderer.getStream()) {
                    // We need to disable the renderer.
                    disableRenderer(renderer);
                } else if (streamResetFlags[i]) {
                    // The renderer will continue to consume from its current stream, but needs to be reset.
                    renderer.resetPosition(rendererPositionUs);
                }
            }
        }
        enableRenderers(rendererWasEnabledFlags);
    } else {
        // Release and re-prepare/buffer periods after the one whose selection changed.
        queue.removeAfter(periodHolder);
        if (periodHolder.prepared) {
            long loadingPeriodPositionUs = max(periodHolder.info.startPositionUs, periodHolder.toPeriodTime(rendererPositionUs));
            periodHolder.applyTrackSelection(newTrackSelectorResult, loadingPeriodPositionUs, false);
        }
    }
    handleLoadingMediaPeriodChanged(/* loadingTrackSelectionChanged= */
    true);
    if (playbackInfo.playbackState != Player.STATE_ENDED) {
        maybeContinueLoading();
        updatePlaybackPositions();
        handler.sendEmptyMessage(MSG_DO_SOME_WORK);
    }
}
Also used : TrackSelectorResult(com.google.android.exoplayer2.trackselection.TrackSelectorResult) TextRenderer(com.google.android.exoplayer2.text.TextRenderer) SampleStream(com.google.android.exoplayer2.source.SampleStream)

Example 14 with Renderer

use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.

the class ExoPlayerImplInternal method replaceStreamsOrDisableRendererForTransition.

private boolean replaceStreamsOrDisableRendererForTransition() throws ExoPlaybackException {
    MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
    TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.getTrackSelectorResult();
    boolean needsToWaitForRendererToEnd = false;
    for (int i = 0; i < renderers.length; i++) {
        Renderer renderer = renderers[i];
        if (!isRendererEnabled(renderer)) {
            continue;
        }
        boolean rendererIsReadingOldStream = renderer.getStream() != readingPeriodHolder.sampleStreams[i];
        boolean rendererShouldBeEnabled = newTrackSelectorResult.isRendererEnabled(i);
        if (rendererShouldBeEnabled && !rendererIsReadingOldStream) {
            // All done.
            continue;
        }
        if (!renderer.isCurrentStreamFinal()) {
            // The renderer stream is not final, so we can replace the sample streams immediately.
            Format[] formats = getFormats(newTrackSelectorResult.selections[i]);
            renderer.replaceStream(formats, readingPeriodHolder.sampleStreams[i], readingPeriodHolder.getStartPositionRendererTime(), readingPeriodHolder.getRendererOffset());
        } else if (renderer.isEnded()) {
            // The renderer has finished playback, so we can disable it now.
            disableRenderer(renderer);
        } else {
            // We need to wait until rendering finished before disabling the renderer.
            needsToWaitForRendererToEnd = true;
        }
    }
    return !needsToWaitForRendererToEnd;
}
Also used : TrackSelectorResult(com.google.android.exoplayer2.trackselection.TrackSelectorResult) TextRenderer(com.google.android.exoplayer2.text.TextRenderer)

Example 15 with Renderer

use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.

the class DefaultRenderersFactory method createRenderers.

@Override
public Renderer[] createRenderers(Handler eventHandler, VideoRendererEventListener videoRendererEventListener, AudioRendererEventListener audioRendererEventListener, TextOutput textRendererOutput, MetadataOutput metadataRendererOutput) {
    ArrayList<Renderer> renderersList = new ArrayList<>();
    buildVideoRenderers(context, extensionRendererMode, mediaCodecSelector, enableDecoderFallback, eventHandler, videoRendererEventListener, allowedVideoJoiningTimeMs, renderersList);
    @Nullable AudioSink audioSink = buildAudioSink(context, enableFloatOutput, enableAudioTrackPlaybackParams, enableOffload);
    if (audioSink != null) {
        buildAudioRenderers(context, extensionRendererMode, mediaCodecSelector, enableDecoderFallback, audioSink, eventHandler, audioRendererEventListener, renderersList);
    }
    buildTextRenderers(context, textRendererOutput, eventHandler.getLooper(), extensionRendererMode, renderersList);
    buildMetadataRenderers(context, metadataRendererOutput, eventHandler.getLooper(), extensionRendererMode, renderersList);
    buildCameraMotionRenderers(context, extensionRendererMode, renderersList);
    buildMiscellaneousRenderers(context, eventHandler, extensionRendererMode, renderersList);
    return renderersList.toArray(new Renderer[0]);
}
Also used : DefaultAudioSink(com.google.android.exoplayer2.audio.DefaultAudioSink) AudioSink(com.google.android.exoplayer2.audio.AudioSink) CameraMotionRenderer(com.google.android.exoplayer2.video.spherical.CameraMotionRenderer) MediaCodecAudioRenderer(com.google.android.exoplayer2.audio.MediaCodecAudioRenderer) MetadataRenderer(com.google.android.exoplayer2.metadata.MetadataRenderer) MediaCodecVideoRenderer(com.google.android.exoplayer2.video.MediaCodecVideoRenderer) TextRenderer(com.google.android.exoplayer2.text.TextRenderer) ArrayList(java.util.ArrayList) 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