Search in sources :

Example 11 with ExoTrackSelection

use of com.google.android.exoplayer2.trackselection.ExoTrackSelection in project ExoPlayer by google.

the class DashMediaPeriod method buildSampleStream.

private ChunkSampleStream<DashChunkSource> buildSampleStream(TrackGroupInfo trackGroupInfo, ExoTrackSelection selection, long positionUs) {
    int embeddedTrackCount = 0;
    boolean enableEventMessageTrack = trackGroupInfo.embeddedEventMessageTrackGroupIndex != C.INDEX_UNSET;
    TrackGroup embeddedEventMessageTrackGroup = null;
    if (enableEventMessageTrack) {
        embeddedEventMessageTrackGroup = trackGroups.get(trackGroupInfo.embeddedEventMessageTrackGroupIndex);
        embeddedTrackCount++;
    }
    boolean enableClosedCaptionTrack = trackGroupInfo.embeddedClosedCaptionTrackGroupIndex != C.INDEX_UNSET;
    TrackGroup embeddedClosedCaptionTrackGroup = null;
    if (enableClosedCaptionTrack) {
        embeddedClosedCaptionTrackGroup = trackGroups.get(trackGroupInfo.embeddedClosedCaptionTrackGroupIndex);
        embeddedTrackCount += embeddedClosedCaptionTrackGroup.length;
    }
    Format[] embeddedTrackFormats = new Format[embeddedTrackCount];
    int[] embeddedTrackTypes = new int[embeddedTrackCount];
    embeddedTrackCount = 0;
    if (enableEventMessageTrack) {
        embeddedTrackFormats[embeddedTrackCount] = embeddedEventMessageTrackGroup.getFormat(0);
        embeddedTrackTypes[embeddedTrackCount] = C.TRACK_TYPE_METADATA;
        embeddedTrackCount++;
    }
    List<Format> embeddedClosedCaptionTrackFormats = new ArrayList<>();
    if (enableClosedCaptionTrack) {
        for (int i = 0; i < embeddedClosedCaptionTrackGroup.length; i++) {
            embeddedTrackFormats[embeddedTrackCount] = embeddedClosedCaptionTrackGroup.getFormat(i);
            embeddedTrackTypes[embeddedTrackCount] = C.TRACK_TYPE_TEXT;
            embeddedClosedCaptionTrackFormats.add(embeddedTrackFormats[embeddedTrackCount]);
            embeddedTrackCount++;
        }
    }
    PlayerTrackEmsgHandler trackPlayerEmsgHandler = manifest.dynamic && enableEventMessageTrack ? playerEmsgHandler.newPlayerTrackEmsgHandler() : null;
    DashChunkSource chunkSource = chunkSourceFactory.createDashChunkSource(manifestLoaderErrorThrower, manifest, baseUrlExclusionList, periodIndex, trackGroupInfo.adaptationSetIndices, selection, trackGroupInfo.trackType, elapsedRealtimeOffsetMs, enableEventMessageTrack, embeddedClosedCaptionTrackFormats, trackPlayerEmsgHandler, transferListener, playerId);
    ChunkSampleStream<DashChunkSource> stream = new ChunkSampleStream<>(trackGroupInfo.trackType, embeddedTrackTypes, embeddedTrackFormats, chunkSource, this, allocator, positionUs, drmSessionManager, drmEventDispatcher, loadErrorHandlingPolicy, mediaSourceEventDispatcher);
    synchronized (this) {
        // The map is also accessed on the loading thread so synchronize access.
        trackEmsgHandlerBySampleStream.put(stream, trackPlayerEmsgHandler);
    }
    return stream;
}
Also used : Format(com.google.android.exoplayer2.Format) ChunkSampleStream(com.google.android.exoplayer2.source.chunk.ChunkSampleStream) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) ArrayList(java.util.ArrayList) PlayerTrackEmsgHandler(com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerTrackEmsgHandler)

Example 12 with ExoTrackSelection

use of com.google.android.exoplayer2.trackselection.ExoTrackSelection in project ExoPlayer by google.

the class DashMediaPeriod method selectNewStreams.

private void selectNewStreams(ExoTrackSelection[] selections, SampleStream[] streams, boolean[] streamResetFlags, long positionUs, int[] streamIndexToTrackGroupIndex) {
    // Create newly selected primary and event streams.
    for (int i = 0; i < selections.length; i++) {
        ExoTrackSelection selection = selections[i];
        if (selection == null) {
            continue;
        }
        if (streams[i] == null) {
            // Create new stream for selection.
            streamResetFlags[i] = true;
            int trackGroupIndex = streamIndexToTrackGroupIndex[i];
            TrackGroupInfo trackGroupInfo = trackGroupInfos[trackGroupIndex];
            if (trackGroupInfo.trackGroupCategory == TrackGroupInfo.CATEGORY_PRIMARY) {
                streams[i] = buildSampleStream(trackGroupInfo, selection, positionUs);
            } else if (trackGroupInfo.trackGroupCategory == TrackGroupInfo.CATEGORY_MANIFEST_EVENTS) {
                EventStream eventStream = eventStreams.get(trackGroupInfo.eventStreamGroupIndex);
                Format format = selection.getTrackGroup().getFormat(0);
                streams[i] = new EventSampleStream(eventStream, format, manifest.dynamic);
            }
        } else if (streams[i] instanceof ChunkSampleStream) {
            // Update selection in existing stream.
            @SuppressWarnings("unchecked") ChunkSampleStream<DashChunkSource> stream = (ChunkSampleStream<DashChunkSource>) streams[i];
            stream.getChunkSource().updateTrackSelection(selection);
        }
    }
    // pass if the index of the primary stream is greater than the index of the embedded stream.
    for (int i = 0; i < selections.length; i++) {
        if (streams[i] == null && selections[i] != null) {
            int trackGroupIndex = streamIndexToTrackGroupIndex[i];
            TrackGroupInfo trackGroupInfo = trackGroupInfos[trackGroupIndex];
            if (trackGroupInfo.trackGroupCategory == TrackGroupInfo.CATEGORY_EMBEDDED) {
                int primaryStreamIndex = getPrimaryStreamIndex(i, streamIndexToTrackGroupIndex);
                if (primaryStreamIndex == C.INDEX_UNSET) {
                    // If an embedded track is selected without the corresponding primary track, create an
                    // empty sample stream instead.
                    streams[i] = new EmptySampleStream();
                } else {
                    streams[i] = ((ChunkSampleStream) streams[primaryStreamIndex]).selectEmbeddedTrack(positionUs, trackGroupInfo.trackType);
                }
            }
        }
    }
}
Also used : Format(com.google.android.exoplayer2.Format) EmptySampleStream(com.google.android.exoplayer2.source.EmptySampleStream) ChunkSampleStream(com.google.android.exoplayer2.source.chunk.ChunkSampleStream) ExoTrackSelection(com.google.android.exoplayer2.trackselection.ExoTrackSelection) EventStream(com.google.android.exoplayer2.source.dash.manifest.EventStream)

Example 13 with ExoTrackSelection

use of com.google.android.exoplayer2.trackselection.ExoTrackSelection in project ExoPlayer by google.

the class HlsSampleStreamWrapper method selectTracks.

/**
 * Called by the parent {@link HlsMediaPeriod} when a track selection occurs.
 *
 * @param selections The renderer track selections.
 * @param mayRetainStreamFlags Flags indicating whether the existing sample stream can be retained
 *     for each selection. A {@code true} value indicates that the selection is unchanged, and
 *     that the caller does not require that the sample stream be recreated.
 * @param streams The existing sample streams, which will be updated to reflect the provided
 *     selections.
 * @param streamResetFlags Will be updated to indicate new sample streams, and sample streams that
 *     have been retained but with the requirement that the consuming renderer be reset.
 * @param positionUs The current playback position in microseconds.
 * @param forceReset If true then a reset is forced (i.e. a seek will be performed with in-buffer
 *     seeking disabled).
 * @return Whether this wrapper requires the parent {@link HlsMediaPeriod} to perform a seek as
 *     part of the track selection.
 */
public boolean selectTracks(@NullableType ExoTrackSelection[] selections, boolean[] mayRetainStreamFlags, @NullableType SampleStream[] streams, boolean[] streamResetFlags, long positionUs, boolean forceReset) {
    assertIsPrepared();
    int oldEnabledTrackGroupCount = enabledTrackGroupCount;
    // Deselect old tracks.
    for (int i = 0; i < selections.length; i++) {
        HlsSampleStream stream = (HlsSampleStream) streams[i];
        if (stream != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
            enabledTrackGroupCount--;
            stream.unbindSampleQueue();
            streams[i] = null;
        }
    }
    // We'll always need to seek if we're being forced to reset, or if this is a first selection to
    // a position other than the one we started preparing with, or if we're making a selection
    // having previously disabled all tracks.
    boolean seekRequired = forceReset || (seenFirstTrackSelection ? oldEnabledTrackGroupCount == 0 : positionUs != lastSeekPositionUs);
    // Get the old (i.e. current before the loop below executes) primary track selection. The new
    // primary selection will equal the old one unless it's changed in the loop.
    ExoTrackSelection oldPrimaryTrackSelection = chunkSource.getTrackSelection();
    ExoTrackSelection primaryTrackSelection = oldPrimaryTrackSelection;
    // Select new tracks.
    for (int i = 0; i < selections.length; i++) {
        ExoTrackSelection selection = selections[i];
        if (selection == null) {
            continue;
        }
        int trackGroupIndex = trackGroups.indexOf(selection.getTrackGroup());
        if (trackGroupIndex == primaryTrackGroupIndex) {
            primaryTrackSelection = selection;
            chunkSource.setTrackSelection(selection);
        }
        if (streams[i] == null) {
            enabledTrackGroupCount++;
            streams[i] = new HlsSampleStream(this, trackGroupIndex);
            streamResetFlags[i] = true;
            if (trackGroupToSampleQueueIndex != null) {
                ((HlsSampleStream) streams[i]).bindSampleQueue();
                // If there's still a chance of avoiding a seek, try and seek within the sample queue.
                if (!seekRequired) {
                    SampleQueue sampleQueue = sampleQueues[trackGroupToSampleQueueIndex[trackGroupIndex]];
                    // A seek can be avoided if we're able to seek to the current playback position in
                    // the sample queue, or if we haven't read anything from the queue since the previous
                    // seek (this case is common for sparse tracks such as metadata tracks). In all other
                    // cases a seek is required.
                    seekRequired = !sampleQueue.seekTo(positionUs, /* allowTimeBeyondBuffer= */
                    true) && sampleQueue.getReadIndex() != 0;
                }
            }
        }
    }
    if (enabledTrackGroupCount == 0) {
        chunkSource.reset();
        downstreamTrackFormat = null;
        pendingResetUpstreamFormats = true;
        mediaChunks.clear();
        if (loader.isLoading()) {
            if (sampleQueuesBuilt) {
                // Discard as much as we can synchronously.
                for (SampleQueue sampleQueue : sampleQueues) {
                    sampleQueue.discardToEnd();
                }
            }
            loader.cancelLoading();
        } else {
            resetSampleQueues();
        }
    } else {
        if (!mediaChunks.isEmpty() && !Util.areEqual(primaryTrackSelection, oldPrimaryTrackSelection)) {
            // The primary track selection has changed and we have buffered media. The buffered media
            // may need to be discarded.
            boolean primarySampleQueueDirty = false;
            if (!seenFirstTrackSelection) {
                long bufferedDurationUs = positionUs < 0 ? -positionUs : 0;
                HlsMediaChunk lastMediaChunk = getLastMediaChunk();
                MediaChunkIterator[] mediaChunkIterators = chunkSource.createMediaChunkIterators(lastMediaChunk, positionUs);
                primaryTrackSelection.updateSelectedTrack(positionUs, bufferedDurationUs, C.TIME_UNSET, readOnlyMediaChunks, mediaChunkIterators);
                int chunkIndex = chunkSource.getTrackGroup().indexOf(lastMediaChunk.trackFormat);
                if (primaryTrackSelection.getSelectedIndexInTrackGroup() != chunkIndex) {
                    // This is the first selection and the chunk loaded during preparation does not match
                    // the initially selected format.
                    primarySampleQueueDirty = true;
                }
            } else {
                // The primary sample queue contains media buffered for the old primary track selection.
                primarySampleQueueDirty = true;
            }
            if (primarySampleQueueDirty) {
                forceReset = true;
                seekRequired = true;
                pendingResetUpstreamFormats = true;
            }
        }
        if (seekRequired) {
            seekToUs(positionUs, forceReset);
            // We'll need to reset renderers consuming from all streams due to the seek.
            for (int i = 0; i < streams.length; i++) {
                if (streams[i] != null) {
                    streamResetFlags[i] = true;
                }
            }
        }
    }
    updateSampleStreams(streams);
    seenFirstTrackSelection = true;
    return seekRequired;
}
Also used : SampleQueue(com.google.android.exoplayer2.source.SampleQueue) MediaChunkIterator(com.google.android.exoplayer2.source.chunk.MediaChunkIterator) ExoTrackSelection(com.google.android.exoplayer2.trackselection.ExoTrackSelection)

Example 14 with ExoTrackSelection

use of com.google.android.exoplayer2.trackselection.ExoTrackSelection in project ExoPlayer by google.

the class MediaPeriodAsserts method assertGetStreamKeysAndManifestFilterIntegration.

/**
 * Asserts that the values returns by {@link MediaPeriod#getStreamKeys(List)} are compatible with
 * a {@link FilterableManifest} using these stream keys.
 *
 * @param mediaPeriodFactory A factory to create a {@link MediaPeriod} based on a manifest.
 * @param manifest The manifest which is to be tested.
 * @param periodIndex The index of period in the manifest.
 * @param ignoredMimeType Optional mime type whose existence in the filtered track groups is not
 *     asserted.
 */
public static <T extends FilterableManifest<T>> void assertGetStreamKeysAndManifestFilterIntegration(FilterableManifestMediaPeriodFactory<T> mediaPeriodFactory, T manifest, int periodIndex, @Nullable String ignoredMimeType) {
    MediaPeriod mediaPeriod = mediaPeriodFactory.createMediaPeriod(manifest, periodIndex);
    TrackGroupArray trackGroupArray = prepareAndGetTrackGroups(mediaPeriod);
    // Create test vector of query test selections:
    // - One selection with one track per group, two tracks or all tracks.
    // - Two selections with tracks from multiple groups, or tracks from a single group.
    // - Multiple selections with tracks from all groups.
    List<List<ExoTrackSelection>> testSelections = new ArrayList<>();
    for (int i = 0; i < trackGroupArray.length; i++) {
        TrackGroup trackGroup = trackGroupArray.get(i);
        for (int j = 0; j < trackGroup.length; j++) {
            testSelections.add(Collections.singletonList(new TestTrackSelection(trackGroup, j)));
        }
        if (trackGroup.length > 1) {
            testSelections.add(Collections.singletonList(new TestTrackSelection(trackGroup, 0, 1)));
            testSelections.add(Arrays.asList(new ExoTrackSelection[] { new TestTrackSelection(trackGroup, 0), new TestTrackSelection(trackGroup, 1) }));
        }
        if (trackGroup.length > 2) {
            int[] allTracks = new int[trackGroup.length];
            for (int j = 0; j < trackGroup.length; j++) {
                allTracks[j] = j;
            }
            testSelections.add(Collections.singletonList(new TestTrackSelection(trackGroup, allTracks)));
        }
    }
    if (trackGroupArray.length > 1) {
        for (int i = 0; i < trackGroupArray.length - 1; i++) {
            for (int j = i + 1; j < trackGroupArray.length; j++) {
                testSelections.add(Arrays.asList(new ExoTrackSelection[] { new TestTrackSelection(trackGroupArray.get(i), 0), new TestTrackSelection(trackGroupArray.get(j), 0) }));
            }
        }
    }
    if (trackGroupArray.length > 2) {
        List<ExoTrackSelection> selectionsFromAllGroups = new ArrayList<>();
        for (int i = 0; i < trackGroupArray.length; i++) {
            selectionsFromAllGroups.add(new TestTrackSelection(trackGroupArray.get(i), 0));
        }
        testSelections.add(selectionsFromAllGroups);
    }
    // contain at least all requested formats.
    for (List<ExoTrackSelection> testSelection : testSelections) {
        List<StreamKey> streamKeys = mediaPeriod.getStreamKeys(testSelection);
        if (streamKeys.isEmpty()) {
            // Manifests won't be filtered if stream key is empty.
            continue;
        }
        T filteredManifest = manifest.copy(streamKeys);
        // The filtered manifest should only have one period left.
        MediaPeriod filteredMediaPeriod = mediaPeriodFactory.createMediaPeriod(filteredManifest, /* periodIndex= */
        0);
        TrackGroupArray filteredTrackGroupArray = prepareAndGetTrackGroups(filteredMediaPeriod);
        for (ExoTrackSelection trackSelection : testSelection) {
            if (ignoredMimeType != null && ignoredMimeType.equals(trackSelection.getFormat(0).sampleMimeType)) {
                continue;
            }
            Format[] expectedFormats = new Format[trackSelection.length()];
            for (int k = 0; k < trackSelection.length(); k++) {
                expectedFormats[k] = trackSelection.getFormat(k);
            }
            assertOneTrackGroupContainsFormats(filteredTrackGroupArray, expectedFormats);
        }
    }
}
Also used : TrackGroupArray(com.google.android.exoplayer2.source.TrackGroupArray) ArrayList(java.util.ArrayList) Format(com.google.android.exoplayer2.Format) ExoTrackSelection(com.google.android.exoplayer2.trackselection.ExoTrackSelection) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) ArrayList(java.util.ArrayList) List(java.util.List) MediaPeriod(com.google.android.exoplayer2.source.MediaPeriod) StreamKey(com.google.android.exoplayer2.offline.StreamKey)

Example 15 with ExoTrackSelection

use of com.google.android.exoplayer2.trackselection.ExoTrackSelection in project ExoPlayer by google.

the class FakeAdaptiveMediaPeriod method selectTracks.

// Casting sample streams created by this class.
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public long selectTracks(@NullableType ExoTrackSelection[] selections, boolean[] mayRetainStreamFlags, @NullableType SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
    assertThat(prepared).isTrue();
    int rendererCount = selections.length;
    for (int i = 0; i < rendererCount; i++) {
        if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
            ((ChunkSampleStream<FakeChunkSource>) streams[i]).release();
            sampleStreams.remove(streams[i]);
            streams[i] = null;
        }
        if (streams[i] == null && selections[i] != null) {
            ExoTrackSelection selection = selections[i];
            assertThat(selection.length()).isAtLeast(1);
            TrackGroup trackGroup = selection.getTrackGroup();
            assertThat(trackGroupArray.indexOf(trackGroup)).isNotEqualTo(C.INDEX_UNSET);
            int indexInTrackGroup = selection.getIndexInTrackGroup(selection.getSelectedIndex());
            assertThat(indexInTrackGroup).isAtLeast(0);
            assertThat(indexInTrackGroup).isLessThan(trackGroup.length);
            FakeChunkSource chunkSource = chunkSourceFactory.createChunkSource(selection, durationUs, transferListener);
            ChunkSampleStream<FakeChunkSource> sampleStream = new ChunkSampleStream<>(MimeTypes.getTrackType(selection.getSelectedFormat().sampleMimeType), /* embeddedTrackTypes= */
            null, /* embeddedTrackFormats= */
            null, chunkSource, /* callback= */
            this, allocator, positionUs, DrmSessionManager.DRM_UNSUPPORTED, new DrmSessionEventListener.EventDispatcher(), new DefaultLoadErrorHandlingPolicy(/* minimumLoadableRetryCount= */
            3), mediaSourceEventDispatcher);
            streams[i] = sampleStream;
            sampleStreams.add(sampleStream);
            streamResetFlags[i] = true;
        }
    }
    sequenceableLoader = new CompositeSequenceableLoader(sampleStreams.toArray(new ChunkSampleStream[0]));
    return seekToUs(positionUs);
}
Also used : ChunkSampleStream(com.google.android.exoplayer2.source.chunk.ChunkSampleStream) ExoTrackSelection(com.google.android.exoplayer2.trackselection.ExoTrackSelection) DefaultLoadErrorHandlingPolicy(com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) DrmSessionEventListener(com.google.android.exoplayer2.drm.DrmSessionEventListener) CompositeSequenceableLoader(com.google.android.exoplayer2.source.CompositeSequenceableLoader)

Aggregations

ExoTrackSelection (com.google.android.exoplayer2.trackselection.ExoTrackSelection)21 TrackGroup (com.google.android.exoplayer2.source.TrackGroup)9 ArrayList (java.util.ArrayList)8 TrackGroupArray (com.google.android.exoplayer2.source.TrackGroupArray)6 Format (com.google.android.exoplayer2.Format)5 NullableType (org.checkerframework.checker.nullness.compatqual.NullableType)5 StreamKey (com.google.android.exoplayer2.offline.StreamKey)4 ChunkSampleStream (com.google.android.exoplayer2.source.chunk.ChunkSampleStream)4 SuppressLint (android.annotation.SuppressLint)3 Point (android.graphics.Point)3 Nullable (androidx.annotation.Nullable)3 TrackSelectorResult (com.google.android.exoplayer2.trackselection.TrackSelectorResult)3 FormatHolder (com.google.android.exoplayer2.FormatHolder)2 RendererCapabilities (com.google.android.exoplayer2.RendererCapabilities)2 Capabilities (com.google.android.exoplayer2.RendererCapabilities.Capabilities)2 RendererConfiguration (com.google.android.exoplayer2.RendererConfiguration)2 DecoderInputBuffer (com.google.android.exoplayer2.decoder.DecoderInputBuffer)2 EmptySampleStream (com.google.android.exoplayer2.source.EmptySampleStream)2 SampleStream (com.google.android.exoplayer2.source.SampleStream)2 FixedTrackSelection (com.google.android.exoplayer2.trackselection.FixedTrackSelection)2