Search in sources :

Example 1 with TrackSelection

use of androidx.media3.common.TrackSelection in project media by androidx.

the class CastPlayer method updateTracksAndSelectionsAndNotifyIfChanged.

/**
 * Updates the internal tracks and selection and returns whether they have changed.
 */
private boolean updateTracksAndSelectionsAndNotifyIfChanged() {
    if (remoteMediaClient == null) {
        // There is no session. We leave the state of the player as it is now.
        return false;
    }
    MediaStatus mediaStatus = getMediaStatus();
    MediaInfo mediaInfo = mediaStatus != null ? mediaStatus.getMediaInfo() : null;
    List<MediaTrack> castMediaTracks = mediaInfo != null ? mediaInfo.getMediaTracks() : null;
    if (castMediaTracks == null || castMediaTracks.isEmpty()) {
        boolean hasChanged = !currentTrackGroups.isEmpty();
        currentTrackGroups = TrackGroupArray.EMPTY;
        currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
        currentTracksInfo = TracksInfo.EMPTY;
        return hasChanged;
    }
    long[] activeTrackIds = mediaStatus.getActiveTrackIds();
    if (activeTrackIds == null) {
        activeTrackIds = EMPTY_TRACK_ID_ARRAY;
    }
    TrackGroup[] trackGroups = new TrackGroup[castMediaTracks.size()];
    @NullableType TrackSelection[] trackSelections = new TrackSelection[RENDERER_COUNT];
    TracksInfo.TrackGroupInfo[] trackGroupInfos = new TracksInfo.TrackGroupInfo[castMediaTracks.size()];
    for (int i = 0; i < castMediaTracks.size(); i++) {
        MediaTrack mediaTrack = castMediaTracks.get(i);
        trackGroups[i] = new TrackGroup(/* id= */
        Integer.toString(i), CastUtils.mediaTrackToFormat(mediaTrack));
        long id = mediaTrack.getId();
        @C.TrackType int trackType = MimeTypes.getTrackType(mediaTrack.getContentType());
        int rendererIndex = getRendererIndexForTrackType(trackType);
        boolean supported = rendererIndex != C.INDEX_UNSET;
        boolean selected = isTrackActive(id, activeTrackIds) && supported && trackSelections[rendererIndex] == null;
        if (selected) {
            trackSelections[rendererIndex] = new CastTrackSelection(trackGroups[i]);
        }
        @C.FormatSupport int[] trackSupport = new int[] { supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE };
        final boolean[] trackSelected = new boolean[] { selected };
        trackGroupInfos[i] = new TracksInfo.TrackGroupInfo(trackGroups[i], trackSupport, trackType, trackSelected);
    }
    TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups);
    TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections);
    TracksInfo newTracksInfo = new TracksInfo(ImmutableList.copyOf(trackGroupInfos));
    if (!newTrackGroups.equals(currentTrackGroups) || !newTrackSelections.equals(currentTrackSelection) || !newTracksInfo.equals(currentTracksInfo)) {
        currentTrackSelection = newTrackSelections;
        currentTrackGroups = newTrackGroups;
        currentTracksInfo = newTracksInfo;
        return true;
    }
    return false;
}
Also used : TrackGroupArray(androidx.media3.common.TrackGroupArray) NullableType(org.checkerframework.checker.nullness.compatqual.NullableType) TracksInfo(androidx.media3.common.TracksInfo) TrackSelectionArray(androidx.media3.common.TrackSelectionArray) MediaTrack(com.google.android.gms.cast.MediaTrack) MediaInfo(com.google.android.gms.cast.MediaInfo) TrackGroup(androidx.media3.common.TrackGroup) TrackSelection(androidx.media3.common.TrackSelection) MediaStatus(com.google.android.gms.cast.MediaStatus)

Example 2 with TrackSelection

use of androidx.media3.common.TrackSelection in project media by androidx.

the class DefaultTrackSelector method maybeConfigureRenderersForTunneling.

// Utility methods.
/**
 * Determines whether tunneling can be enabled, replacing {@link RendererConfiguration}s in {@code
 * rendererConfigurations} with configurations that enable tunneling on the appropriate renderers
 * if so.
 *
 * @param mappedTrackInfo Mapped track information.
 * @param renderererFormatSupports The {@link Capabilities} for each mapped track, indexed by
 *     renderer, track group and track (in that order).
 * @param rendererConfigurations The renderer configurations. Configurations may be replaced with
 *     ones that enable tunneling as a result of this call.
 * @param trackSelections The renderer track selections.
 */
private static void maybeConfigureRenderersForTunneling(MappedTrackInfo mappedTrackInfo, @Capabilities int[][][] renderererFormatSupports, @NullableType RendererConfiguration[] rendererConfigurations, @NullableType ExoTrackSelection[] trackSelections) {
    // Check whether we can enable tunneling. To enable tunneling we require exactly one audio and
    // one video renderer to support tunneling and have a selection.
    int tunnelingAudioRendererIndex = -1;
    int tunnelingVideoRendererIndex = -1;
    boolean enableTunneling = true;
    for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
        int rendererType = mappedTrackInfo.getRendererType(i);
        ExoTrackSelection trackSelection = trackSelections[i];
        if ((rendererType == C.TRACK_TYPE_AUDIO || rendererType == C.TRACK_TYPE_VIDEO) && trackSelection != null) {
            if (rendererSupportsTunneling(renderererFormatSupports[i], mappedTrackInfo.getTrackGroups(i), trackSelection)) {
                if (rendererType == C.TRACK_TYPE_AUDIO) {
                    if (tunnelingAudioRendererIndex != -1) {
                        enableTunneling = false;
                        break;
                    } else {
                        tunnelingAudioRendererIndex = i;
                    }
                } else {
                    if (tunnelingVideoRendererIndex != -1) {
                        enableTunneling = false;
                        break;
                    } else {
                        tunnelingVideoRendererIndex = i;
                    }
                }
            }
        }
    }
    enableTunneling &= tunnelingAudioRendererIndex != -1 && tunnelingVideoRendererIndex != -1;
    if (enableTunneling) {
        RendererConfiguration tunnelingRendererConfiguration = new RendererConfiguration(/* tunneling= */
        true);
        rendererConfigurations[tunnelingAudioRendererIndex] = tunnelingRendererConfiguration;
        rendererConfigurations[tunnelingVideoRendererIndex] = tunnelingRendererConfiguration;
    }
}
Also used : RendererConfiguration(androidx.media3.exoplayer.RendererConfiguration) SuppressLint(android.annotation.SuppressLint) Point(android.graphics.Point)

Example 3 with TrackSelection

use of androidx.media3.common.TrackSelection in project media by androidx.

the class ExoPlayerTest method allActivatedTrackSelectionAreReleasedWhenTrackSelectionsAreReused.

@Test
public void allActivatedTrackSelectionAreReleasedWhenTrackSelectionsAreReused() throws Exception {
    MediaSource mediaSource = new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT, ExoPlayerTestRunner.AUDIO_FORMAT);
    FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO);
    FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO);
    final FakeTrackSelector trackSelector = new FakeTrackSelector(/* mayReuseTrackSelection= */
    true);
    ActionSchedule disableTrackAction = new ActionSchedule.Builder(TAG).pause().waitForPlaybackState(Player.STATE_READY).disableRenderer(0).play().build();
    new ExoPlayerTestRunner.Builder(context).setMediaSources(mediaSource).setRenderers(videoRenderer, audioRenderer).setTrackSelector(trackSelector).setActionSchedule(disableTrackAction).build().start().blockUntilEnded(TIMEOUT_MS);
    List<FakeTrackSelection> createdTrackSelections = trackSelector.getAllTrackSelections();
    int numSelectionsEnabled = 0;
    // Assert that all tracks selection are disabled at the end of the playback.
    for (FakeTrackSelection trackSelection : createdTrackSelections) {
        assertThat(trackSelection.isEnabled).isFalse();
        numSelectionsEnabled += trackSelection.enableCount;
    }
    // There are 2 renderers, and track selections are made twice. The second time one renderer is
    // disabled, and the selector re-uses the previous selection for the enabled renderer. So we
    // expect two track selections, one of which will have been enabled twice.
    assertThat(createdTrackSelections).hasSize(2);
    assertThat(numSelectionsEnabled).isEqualTo(3);
}
Also used : FakeRenderer(androidx.media3.test.utils.FakeRenderer) FakeTrackSelection(androidx.media3.test.utils.FakeTrackSelection) CompositeMediaSource(androidx.media3.exoplayer.source.CompositeMediaSource) ClippingMediaSource(androidx.media3.exoplayer.source.ClippingMediaSource) FakeMediaSource(androidx.media3.test.utils.FakeMediaSource) MaskingMediaSource(androidx.media3.exoplayer.source.MaskingMediaSource) ServerSideAdInsertionMediaSource(androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource) FakeAdaptiveMediaSource(androidx.media3.test.utils.FakeAdaptiveMediaSource) ConcatenatingMediaSource(androidx.media3.exoplayer.source.ConcatenatingMediaSource) MediaSource(androidx.media3.exoplayer.source.MediaSource) FakeMediaSource(androidx.media3.test.utils.FakeMediaSource) FakeTrackSelector(androidx.media3.test.utils.FakeTrackSelector) ActionSchedule(androidx.media3.test.utils.ActionSchedule) FakeTimeline(androidx.media3.test.utils.FakeTimeline) ExoPlayerTestRunner(androidx.media3.test.utils.ExoPlayerTestRunner) Test(org.junit.Test)

Example 4 with TrackSelection

use of androidx.media3.common.TrackSelection in project media by androidx.

the class DefaultDashChunkSource method onChunkLoadError.

@Override
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo, LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
    if (!cancelable) {
        return false;
    }
    if (playerTrackEmsgHandler != null && playerTrackEmsgHandler.onChunkLoadError(chunk)) {
        return true;
    }
    // Workaround for missing segment at the end of the period
    if (!manifest.dynamic && chunk instanceof MediaChunk && loadErrorInfo.exception instanceof InvalidResponseCodeException && ((InvalidResponseCodeException) loadErrorInfo.exception).responseCode == 404) {
        RepresentationHolder representationHolder = representationHolders[trackSelection.indexOf(chunk.trackFormat)];
        long segmentCount = representationHolder.getSegmentCount();
        if (segmentCount != DashSegmentIndex.INDEX_UNBOUNDED && segmentCount != 0) {
            long lastAvailableSegmentNum = representationHolder.getFirstSegmentNum() + segmentCount - 1;
            if (((MediaChunk) chunk).getNextChunkIndex() > lastAvailableSegmentNum) {
                missingLastSegment = true;
                return true;
            }
        }
    }
    int trackIndex = trackSelection.indexOf(chunk.trackFormat);
    RepresentationHolder representationHolder = representationHolders[trackIndex];
    @Nullable BaseUrl newBaseUrl = baseUrlExclusionList.selectBaseUrl(representationHolder.representation.baseUrls);
    if (newBaseUrl != null && !representationHolder.selectedBaseUrl.equals(newBaseUrl)) {
        // which will use the new base URL.
        return true;
    }
    LoadErrorHandlingPolicy.FallbackOptions fallbackOptions = createFallbackOptions(trackSelection, representationHolder.representation.baseUrls);
    if (!fallbackOptions.isFallbackAvailable(LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK) && !fallbackOptions.isFallbackAvailable(LoadErrorHandlingPolicy.FALLBACK_TYPE_LOCATION)) {
        return false;
    }
    @Nullable LoadErrorHandlingPolicy.FallbackSelection fallbackSelection = loadErrorHandlingPolicy.getFallbackSelectionFor(fallbackOptions, loadErrorInfo);
    if (fallbackSelection == null || !fallbackOptions.isFallbackAvailable(fallbackSelection.type)) {
        // Policy indicated to not use any fallback or a fallback type that is not available.
        return false;
    }
    boolean cancelLoad = false;
    if (fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK) {
        cancelLoad = trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), fallbackSelection.exclusionDurationMs);
    } else if (fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_LOCATION) {
        baseUrlExclusionList.exclude(representationHolder.selectedBaseUrl, fallbackSelection.exclusionDurationMs);
        cancelLoad = true;
    }
    return cancelLoad;
}
Also used : SingleSampleMediaChunk(androidx.media3.exoplayer.source.chunk.SingleSampleMediaChunk) ContainerMediaChunk(androidx.media3.exoplayer.source.chunk.ContainerMediaChunk) MediaChunk(androidx.media3.exoplayer.source.chunk.MediaChunk) BaseUrl(androidx.media3.exoplayer.dash.manifest.BaseUrl) LoadErrorHandlingPolicy(androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy) InvalidResponseCodeException(androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException) Nullable(androidx.annotation.Nullable)

Example 5 with TrackSelection

use of androidx.media3.common.TrackSelection in project media by androidx.

the class RtspMediaPeriod method selectTracks.

@Override
public long selectTracks(@NullableType ExoTrackSelection[] selections, boolean[] mayRetainStreamFlags, @NullableType SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
    // Input array streams contains the streams selected in the previous track selection.
    for (int i = 0; i < selections.length; i++) {
        if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
            streams[i] = null;
        }
    }
    // Select new tracks.
    selectedLoadInfos.clear();
    for (int i = 0; i < selections.length; i++) {
        TrackSelection selection = selections[i];
        if (selection == null) {
            continue;
        }
        TrackGroup trackGroup = selection.getTrackGroup();
        int trackGroupIndex = checkNotNull(trackGroups).indexOf(trackGroup);
        selectedLoadInfos.add(checkNotNull(rtspLoaderWrappers.get(trackGroupIndex)).loadInfo);
        // Find the sampleStreamWrapper that contains this track group.
        if (trackGroups.contains(trackGroup)) {
            if (streams[i] == null) {
                streams[i] = new SampleStreamImpl(trackGroupIndex);
                // Update flag for newly created SampleStream.
                streamResetFlags[i] = true;
            }
        }
    }
    // Cancel non-selected loadables.
    for (int i = 0; i < rtspLoaderWrappers.size(); i++) {
        RtspLoaderWrapper loadControl = rtspLoaderWrappers.get(i);
        if (!selectedLoadInfos.contains(loadControl.loadInfo)) {
            loadControl.cancelLoad();
        }
    }
    trackSelected = true;
    maybeSetupTracks();
    return positionUs;
}
Also used : TrackGroup(androidx.media3.common.TrackGroup) TrackSelection(androidx.media3.common.TrackSelection) ExoTrackSelection(androidx.media3.exoplayer.trackselection.ExoTrackSelection)

Aggregations

ExoTrackSelection (androidx.media3.exoplayer.trackselection.ExoTrackSelection)8 TrackGroup (androidx.media3.common.TrackGroup)7 TrackGroupArray (androidx.media3.common.TrackGroupArray)6 TrackSelection (androidx.media3.common.TrackSelection)5 Test (org.junit.Test)5 StreamKey (androidx.media3.common.StreamKey)4 ClippingMediaSource (androidx.media3.exoplayer.source.ClippingMediaSource)4 CompositeMediaSource (androidx.media3.exoplayer.source.CompositeMediaSource)4 ConcatenatingMediaSource (androidx.media3.exoplayer.source.ConcatenatingMediaSource)4 MaskingMediaSource (androidx.media3.exoplayer.source.MaskingMediaSource)4 MediaSource (androidx.media3.exoplayer.source.MediaSource)4 ServerSideAdInsertionMediaSource (androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource)4 FakeAdaptiveMediaSource (androidx.media3.test.utils.FakeAdaptiveMediaSource)4 FakeMediaSource (androidx.media3.test.utils.FakeMediaSource)4 FakeRenderer (androidx.media3.test.utils.FakeRenderer)4 FakeTimeline (androidx.media3.test.utils.FakeTimeline)4 FakeTrackSelection (androidx.media3.test.utils.FakeTrackSelection)4 FakeTrackSelector (androidx.media3.test.utils.FakeTrackSelector)4 ArrayList (java.util.ArrayList)4 TracksInfo (androidx.media3.common.TracksInfo)3