@Override public int getRendererType(int index) { return renderers[index].getTrackType(); }
private void disableRenderer(Renderer renderer) throws ExoPlaybackException { mediaClock.onRendererDisabled(renderer); ensureStopped(renderer); renderer.disable(); }
private boolean isUsingRendererClock() { // Use the renderer clock if the providing renderer has not ended or needs the next sample // stream to reenter the ready state. The latter case uses the standalone clock to avoid getting // stuck if tracks in the current period have uneven durations. // See: https://github.com/google/ExoPlayer/issues/1874. return rendererClockSource != null && !rendererClockSource.isEnded() && (rendererClockSource.isReady() || !rendererClockSource.hasReadStreamToEnd()); }
if (sampleStream != null && renderer.getStream() == sampleStream && renderer.hasReadStreamToEnd()) { renderer.setCurrentStreamFinal(); Renderer renderer = renderers[i]; SampleStream sampleStream = readingPeriodHolder.sampleStreams[i]; if (renderer.getStream() != sampleStream || (sampleStream != null && !renderer.hasReadStreamToEnd())) { renderer.setCurrentStreamFinal(); } else if (!renderer.isCurrentStreamFinal()) { TrackSelection newSelection = newTrackSelectorResult.selections.get(i); boolean newRendererEnabled = newTrackSelectorResult.isRendererEnabled(i); renderer.replaceStream(formats, readingPeriodHolder.sampleStreams[i], readingPeriodHolder.getRendererOffset()); } else { renderer.setCurrentStreamFinal();
renderer.render(rendererPositionUs, rendererPositionElapsedRealtimeUs); renderersEnded = renderersEnded && renderer.isEnded(); boolean rendererReadyOrEnded = renderer.isReady() || renderer.isEnded() || rendererWaitingForNextStream(renderer); if (!rendererReadyOrEnded) { renderer.maybeThrowStreamError(); renderer.maybeThrowStreamError();
private void enableRenderer( int rendererIndex, boolean wasRendererEnabled, int enabledRendererIndex) throws ExoPlaybackException { MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod(); Renderer renderer = renderers[rendererIndex]; enabledRenderers[enabledRendererIndex] = renderer; if (renderer.getState() == Renderer.STATE_DISABLED) { RendererConfiguration rendererConfiguration = playingPeriodHolder.trackSelectorResult.rendererConfigurations[rendererIndex]; TrackSelection newSelection = playingPeriodHolder.trackSelectorResult.selections.get( rendererIndex); Format[] formats = getFormats(newSelection); // The renderer needs enabling with its new track selection. boolean playing = playWhenReady && playbackInfo.playbackState == Player.STATE_READY; // Consider as joining only if the renderer was previously disabled. boolean joining = !wasRendererEnabled && playing; // Enable the renderer. renderer.enable(rendererConfiguration, formats, playingPeriodHolder.sampleStreams[rendererIndex], rendererPositionUs, joining, playingPeriodHolder.getRendererOffset()); mediaClock.onRendererEnabled(renderer); // Start the renderer if playing. if (playing) { renderer.start(); } } }
@SuppressWarnings("ParameterNotNullable") private void updatePlayingPeriodRenderers(@Nullable MediaPeriodHolder oldPlayingPeriodHolder) throws ExoPlaybackException { MediaPeriodHolder newPlayingPeriodHolder = queue.getPlayingPeriod(); if (newPlayingPeriodHolder == null || oldPlayingPeriodHolder == newPlayingPeriodHolder) { return; } int enabledRendererCount = 0; boolean[] rendererWasEnabledFlags = new boolean[renderers.length]; for (int i = 0; i < renderers.length; i++) { Renderer renderer = renderers[i]; rendererWasEnabledFlags[i] = renderer.getState() != Renderer.STATE_DISABLED; if (newPlayingPeriodHolder.trackSelectorResult.isRendererEnabled(i)) { enabledRendererCount++; } if (rendererWasEnabledFlags[i] && (!newPlayingPeriodHolder.trackSelectorResult.isRendererEnabled(i) || (renderer.isCurrentStreamFinal() && renderer.getStream() == oldPlayingPeriodHolder.sampleStreams[i]))) { // The renderer should be disabled before playing the next period, either because it's not // needed to play the next period, or because we need to re-enable it as its current stream // is final and it's not reading ahead. disableRenderer(renderer); } } playbackInfo = playbackInfo.copyWithTrackInfo( newPlayingPeriodHolder.trackGroups, newPlayingPeriodHolder.trackSelectorResult); enableRenderers(rendererWasEnabledFlags, enabledRendererCount); }
for (int i = 0; i < renderers.length; i++) { Renderer renderer = renderers[i]; rendererWasEnabledFlags[i] = renderer.getState() != Renderer.STATE_DISABLED; SampleStream sampleStream = playingPeriodHolder.sampleStreams[i]; if (sampleStream != null) { if (sampleStream != renderer.getStream()) { renderer.resetPosition(rendererPositionUs);
rendererCapabilities = new RendererCapabilities[renderers.length]; for (int i = 0; i < renderers.length; i++) { renderers[i].setIndex(i); rendererCapabilities[i] = renderers[i].getCapabilities();
private boolean rendererWaitingForNextStream(Renderer renderer) { MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod(); return readingPeriodHolder.next != null && readingPeriodHolder.next.prepared && renderer.hasReadStreamToEnd(); }
/** * Notifies the media clock that a renderer has been enabled. Starts using the media clock of the * provided renderer if available. * * @param renderer The renderer which has been enabled. * @throws ExoPlaybackException If the renderer provides a media clock and another renderer media * clock is already provided. */ public void onRendererEnabled(Renderer renderer) throws ExoPlaybackException { MediaClock rendererMediaClock = renderer.getMediaClock(); if (rendererMediaClock != null && rendererMediaClock != rendererClock) { if (rendererClock != null) { throw ExoPlaybackException.createForUnexpected( new IllegalStateException("Multiple renderer media clocks enabled.")); } this.rendererClock = rendererMediaClock; this.rendererClockSource = renderer; rendererClock.setPlaybackParameters(standaloneMediaClock.getPlaybackParameters()); ensureSynced(); } }
private void maybeThrowSourceInfoRefreshError() throws IOException { MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod(); if (loadingPeriodHolder != null) { // Defer throwing until we read all available media periods. for (Renderer renderer : enabledRenderers) { if (!renderer.hasReadStreamToEnd()) { return; } } } mediaSource.maybeThrowSourceInfoRefreshError(); }
/** * Calculate target buffer size in bytes based on the selected tracks. The player will try not to * exceed this target buffer. Only used when {@code targetBufferBytes} is {@link C#LENGTH_UNSET}. * * @param renderers The renderers for which the track were selected. * @param trackSelectionArray The selected tracks. * @return The target buffer size in bytes. */ protected int calculateTargetBufferSize( Renderer[] renderers, TrackSelectionArray trackSelectionArray) { int targetBufferSize = 0; for (int i = 0; i < renderers.length; i++) { if (trackSelectionArray.get(i) != null) { targetBufferSize += Util.getDefaultBufferSize(renderers[i].getTrackType()); } } return targetBufferSize; }
private void maybeThrowPeriodPrepareError() throws IOException { MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod(); MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod(); if (loadingPeriodHolder != null && !loadingPeriodHolder.prepared && (readingPeriodHolder == null || readingPeriodHolder.next == loadingPeriodHolder)) { for (Renderer renderer : enabledRenderers) { if (!renderer.hasReadStreamToEnd()) { return; } } loadingPeriodHolder.mediaPeriod.maybeThrowPrepareError(); } }
private void setVideoSurfaceInternal(@Nullable Surface surface, boolean ownsSurface) { // Note: We don't turn this method into a no-op if the surface is being replaced with itself // so as to ensure onRenderedFirstFrame callbacks are still called in this case. List<PlayerMessage> messages = new ArrayList<>(); for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { messages.add( player.createMessage(renderer).setType(C.MSG_SET_SURFACE).setPayload(surface).send()); } } if (this.surface != null && this.surface != surface) { // We're replacing a surface. Block to ensure that it's not accessed after the method returns. try { for (PlayerMessage message : messages) { message.blockUntilDelivered(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // If we created the previous surface, we are responsible for releasing it. if (this.ownsSurface) { this.surface.release(); } } this.surface = surface; this.ownsSurface = ownsSurface; }
private void sendVolumeToRenderers() { float scaledVolume = audioVolume * audioFocusManager.getVolumeMultiplier(); for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { player.createMessage(renderer).setType(C.MSG_SET_VOLUME).setPayload(scaledVolume).send(); } } }
@Override public void setCameraMotionListener(CameraMotionListener listener) { verifyApplicationThread(); cameraMotionListener = listener; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_CAMERA_MOTION) { player .createMessage(renderer) .setType(C.MSG_SET_CAMERA_MOTION_LISTENER) .setPayload(listener) .send(); } } }
@Override public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) { verifyApplicationThread(); for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { player .createMessage(renderer) .setType(C.MSG_SET_AUX_EFFECT_INFO) .setPayload(auxEffectInfo) .send(); } } }
@Override public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { verifyApplicationThread(); videoFrameMetadataListener = listener; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { player .createMessage(renderer) .setType(C.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) .setPayload(listener) .send(); } } }