mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Simplification: Move common logic to TrackSelection
- TrackSelection now exposes the selected formats, ordered by decreasing bandwidth. This removes the need for DASH, SS and HLS to do the sorting individually. - The change also removes the need to reconstruct TrackSelection instances with a different group index in various places (e.g. MergingMediaPeriod). - This is also a step toward potentially packaging the FormatEvaluator inside of the TrackSelection (TBD). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=128159064
This commit is contained in:
parent
6f1b24f10f
commit
692d756ee6
20 changed files with 239 additions and 342 deletions
|
|
@ -109,7 +109,7 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
|
|||
trackGroup.length, trackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false));
|
||||
Log.d(TAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " [");
|
||||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||
String status = getTrackStatusString(trackSelection, groupIndex, trackIndex);
|
||||
String status = getTrackStatusString(trackSelection, trackGroup, trackIndex);
|
||||
String formatSupport = getFormatSupportString(
|
||||
trackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex));
|
||||
Log.d(TAG, " " + status + " Track:" + trackIndex + ", "
|
||||
|
|
@ -353,9 +353,9 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
|
|||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String getTrackStatusString(TrackSelection selection, int groupIndex,
|
||||
private static String getTrackStatusString(TrackSelection selection, TrackGroup group,
|
||||
int trackIndex) {
|
||||
boolean groupEnabled = selection != null && selection.group == groupIndex;
|
||||
boolean groupEnabled = selection != null && selection.group == group;
|
||||
if (groupEnabled) {
|
||||
for (int i = 0; i < selection.length; i++) {
|
||||
if (selection.getTrack(i) == trackIndex) {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ import java.util.Locale;
|
|||
if (trackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex)
|
||||
== RendererCapabilities.FORMAT_HANDLED) {
|
||||
haveSupportedTracks = true;
|
||||
trackView.setTag(Pair.create(groupIndex, trackIndex));
|
||||
trackView.setTag(Pair.create(group, trackIndex));
|
||||
trackView.setOnClickListener(this);
|
||||
} else {
|
||||
trackView.setEnabled(false);
|
||||
|
|
@ -160,7 +160,7 @@ import java.util.Locale;
|
|||
for (int i = 0; i < trackViews.length; i++) {
|
||||
for (int j = 0; j < trackViews[i].length; j++) {
|
||||
trackViews[i][j].setChecked(
|
||||
override != null && override.group == i && override.containsTrack(j));
|
||||
override != null && override.group == trackGroups.get(i) && override.indexOf(j) != -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,11 +194,11 @@ import java.util.Locale;
|
|||
} else {
|
||||
isDisabled = false;
|
||||
@SuppressWarnings("unchecked")
|
||||
Pair<Integer, Integer> tag = (Pair<Integer, Integer>) view.getTag();
|
||||
int groupIndex = tag.first;
|
||||
Pair<TrackGroup, Integer> tag = (Pair<TrackGroup, Integer>) view.getTag();
|
||||
TrackGroup group = tag.first;
|
||||
int trackIndex = tag.second;
|
||||
if (!trackGroupsAdaptive[groupIndex] || override == null) {
|
||||
override = new TrackSelection(groupIndex, trackIndex);
|
||||
if (!trackGroupsAdaptive[trackGroups.indexOf(group)] || override == null) {
|
||||
override = new TrackSelection(group, trackIndex);
|
||||
} else {
|
||||
// The group being modified is adaptive and we already have a non-null override.
|
||||
boolean isEnabled = ((CheckedTextView) view).isChecked();
|
||||
|
|
@ -216,13 +216,13 @@ import java.util.Locale;
|
|||
tracks[trackCount++] = override.getTrack(i);
|
||||
}
|
||||
}
|
||||
override = new TrackSelection(groupIndex, tracks);
|
||||
override = new TrackSelection(group, tracks);
|
||||
}
|
||||
} else {
|
||||
// Add the track to the override.
|
||||
int[] tracks = Arrays.copyOf(override.getTracks(), override.length + 1);
|
||||
tracks[tracks.length - 1] = trackIndex;
|
||||
override = new TrackSelection(groupIndex, tracks);
|
||||
override = new TrackSelection(group, tracks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage;
|
|||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
|
|
@ -705,7 +704,6 @@ import java.util.ArrayList;
|
|||
TrackSelectionArray oldTrackSelections = readingPeriod.trackSelections;
|
||||
readingPeriod = readingPeriod.nextPeriod;
|
||||
TrackSelectionArray newTrackSelections = readingPeriod.trackSelections;
|
||||
TrackGroupArray groups = readingPeriod.mediaPeriod.getTrackGroups();
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
Renderer renderer = renderers[i];
|
||||
TrackSelection oldSelection = oldTrackSelections.get(i);
|
||||
|
|
@ -716,7 +714,7 @@ import java.util.ArrayList;
|
|||
// can be seamless.
|
||||
Format[] formats = new Format[newSelection.length];
|
||||
for (int j = 0; j < formats.length; j++) {
|
||||
formats[j] = groups.get(newSelection.group).getFormat(newSelection.getTrack(j));
|
||||
formats[j] = newSelection.group.getFormat(newSelection.getTrack(j));
|
||||
}
|
||||
renderer.replaceStream(formats, readingPeriod.sampleStreams[i],
|
||||
readingPeriod.offsetUs);
|
||||
|
|
@ -956,7 +954,6 @@ import java.util.ArrayList;
|
|||
throws ExoPlaybackException {
|
||||
enabledRenderers = new Renderer[enabledRendererCount];
|
||||
enabledRendererCount = 0;
|
||||
TrackGroupArray trackGroups = playingPeriod.mediaPeriod.getTrackGroups();
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
Renderer renderer = renderers[i];
|
||||
TrackSelection newSelection = playingPeriod.trackSelections.get(i);
|
||||
|
|
@ -970,7 +967,7 @@ import java.util.ArrayList;
|
|||
// Build an array of formats contained by the selection.
|
||||
Format[] formats = new Format[newSelection.length];
|
||||
for (int j = 0; j < formats.length; j++) {
|
||||
formats[j] = trackGroups.get(newSelection.group).getFormat(newSelection.getTrack(j));
|
||||
formats[j] = newSelection.group.getFormat(newSelection.getTrack(j));
|
||||
}
|
||||
// Enable the renderer.
|
||||
renderer.enable(formats, playingPeriod.sampleStreams[i], internalPositionUs, joining,
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ public final class ExtractorMediaSource implements MediaPeriod, MediaSource,
|
|||
TrackSelection selection = newSelections.get(i);
|
||||
Assertions.checkState(selection.length == 1);
|
||||
Assertions.checkState(selection.getTrack(0) == 0);
|
||||
int track = selection.group;
|
||||
int track = tracks.indexOf(selection.group);
|
||||
Assertions.checkState(!trackEnabledStates[track]);
|
||||
enabledTrackCount++;
|
||||
trackEnabledStates[track] = true;
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ import com.google.android.exoplayer2.C;
|
|||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IdentityHashMap;
|
||||
|
|
@ -183,9 +181,10 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba
|
|||
TrackGroup[] trackGroupArray = new TrackGroup[totalTrackGroupCount];
|
||||
int trackGroupIndex = 0;
|
||||
for (MediaPeriod period : periods) {
|
||||
int periodTrackGroupCount = period.getTrackGroups().length;
|
||||
TrackGroupArray periodTrackGroups = period.getTrackGroups();
|
||||
int periodTrackGroupCount = periodTrackGroups.length;
|
||||
for (int j = 0; j < periodTrackGroupCount; j++) {
|
||||
trackGroupArray[trackGroupIndex++] = period.getTrackGroups().get(j);
|
||||
trackGroupArray[trackGroupIndex++] = periodTrackGroups.get(j);
|
||||
}
|
||||
}
|
||||
trackGroups = new TrackGroupArray(trackGroupArray);
|
||||
|
|
@ -218,12 +217,12 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba
|
|||
// Get the subset of the new selections for the period.
|
||||
ArrayList<TrackSelection> newSelections = new ArrayList<>();
|
||||
int[] newSelectionOriginalIndices = new int[allNewSelections.size()];
|
||||
TrackGroupArray periodTrackGroups = period.getTrackGroups();
|
||||
for (int i = 0; i < allNewSelections.size(); i++) {
|
||||
TrackSelection selection = allNewSelections.get(i);
|
||||
Pair<MediaPeriod, Integer> periodAndGroup = getPeriodAndGroup(selection.group);
|
||||
if (periodAndGroup.first == period) {
|
||||
if (periodTrackGroups.indexOf(selection.group) != -1) {
|
||||
newSelectionOriginalIndices[newSelections.size()] = i;
|
||||
newSelections.add(new TrackSelection(periodAndGroup.second, selection.getTracks()));
|
||||
newSelections.add(selection);
|
||||
}
|
||||
}
|
||||
// Do nothing if nothing has changed, except during the first selection.
|
||||
|
|
@ -239,16 +238,4 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba
|
|||
return newSelections.size() - oldStreams.size();
|
||||
}
|
||||
|
||||
private Pair<MediaPeriod, Integer> getPeriodAndGroup(int group) {
|
||||
int totalTrackGroupCount = 0;
|
||||
for (MediaPeriod period : periods) {
|
||||
int periodTrackGroupCount = period.getTrackGroups().length;
|
||||
if (group < totalTrackGroupCount + periodTrackGroupCount) {
|
||||
return Pair.create(period, group - totalTrackGroupCount);
|
||||
}
|
||||
totalTrackGroupCount += periodTrackGroupCount;
|
||||
}
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,21 @@ public final class TrackGroup {
|
|||
return formats[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the track with the given format in the group.
|
||||
*
|
||||
* @param format The format.
|
||||
* @return The index of the track, or -1 if no such track exists.
|
||||
*/
|
||||
public int indexOf(Format format) {
|
||||
for (int i = 0; i < formats.length; i++) {
|
||||
if (format == formats[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,21 @@ public final class TrackGroupArray {
|
|||
return trackGroups[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a group within the array.
|
||||
*
|
||||
* @param group The group.
|
||||
* @return The index of the group, or -1 if no such group exists.
|
||||
*/
|
||||
public int indexOf(TrackGroup group) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (trackGroups[i] == group) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.dash;
|
||||
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkSource;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.Loader;
|
||||
|
||||
/**
|
||||
|
|
@ -28,7 +28,7 @@ public interface DashChunkSource extends ChunkSource {
|
|||
interface Factory {
|
||||
|
||||
DashChunkSource createDashChunkSource(Loader manifestLoader, DashManifest manifest,
|
||||
int periodIndex, int adaptationSetIndex, TrackGroup trackGroup, int[] tracks,
|
||||
int periodIndex, int adaptationSetIndex, TrackSelection trackSelection,
|
||||
long elapsedRealtimeOffsetMs);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,12 +236,10 @@ import java.util.List;
|
|||
|
||||
private ChunkSampleStream<DashChunkSource> buildSampleStream(TrackSelection selection,
|
||||
long positionUs) {
|
||||
int[] selectedTracks = selection.getTracks();
|
||||
int adaptationSetIndex = trackGroupAdaptationSetIndices[selection.group];
|
||||
int adaptationSetIndex = trackGroupAdaptationSetIndices[trackGroups.indexOf(selection.group)];
|
||||
AdaptationSet adaptationSet = period.adaptationSets.get(adaptationSetIndex);
|
||||
DashChunkSource chunkSource = chunkSourceFactory.createDashChunkSource(loader, manifest, index,
|
||||
adaptationSetIndex, trackGroups.get(selection.group), selectedTracks,
|
||||
elapsedRealtimeOffset);
|
||||
adaptationSetIndex, selection, elapsedRealtimeOffset);
|
||||
return new ChunkSampleStream<>(adaptationSet.type, chunkSource, this, allocator, positionUs,
|
||||
minLoadableRetryCount, eventDispatcher);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,11 @@ package com.google.android.exoplayer2.source.dash;
|
|||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Format.DecreasingBandwidthComparator;
|
||||
import com.google.android.exoplayer2.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
|
||||
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.chunk.Chunk;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
|
||||
|
|
@ -36,6 +34,7 @@ import com.google.android.exoplayer2.source.chunk.SingleSampleMediaChunk;
|
|||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
|
||||
|
|
@ -46,7 +45,6 @@ import com.google.android.exoplayer2.util.Util;
|
|||
import android.os.SystemClock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -67,22 +65,21 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
|
||||
@Override
|
||||
public DashChunkSource createDashChunkSource(Loader manifestLoader, DashManifest manifest,
|
||||
int periodIndex, int adaptationSetIndex, TrackGroup trackGroup, int[] tracks,
|
||||
int periodIndex, int adaptationSetIndex, TrackSelection trackSelection,
|
||||
long elapsedRealtimeOffsetMs) {
|
||||
FormatEvaluator adaptiveEvaluator = tracks.length > 1
|
||||
FormatEvaluator adaptiveEvaluator = trackSelection.length > 1
|
||||
? formatEvaluatorFactory.createFormatEvaluator() : null;
|
||||
DataSource dataSource = dataSourceFactory.createDataSource();
|
||||
return new DefaultDashChunkSource(manifestLoader, manifest, periodIndex, adaptationSetIndex,
|
||||
trackGroup, tracks, dataSource, adaptiveEvaluator, elapsedRealtimeOffsetMs);
|
||||
trackSelection, dataSource, adaptiveEvaluator, elapsedRealtimeOffsetMs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Loader manifestLoader;
|
||||
private final int adaptationSetIndex;
|
||||
private final TrackGroup trackGroup;
|
||||
private final TrackSelection trackSelection;
|
||||
private final RepresentationHolder[] representationHolders;
|
||||
private final Format[] enabledFormats;
|
||||
private final boolean[] adaptiveFormatBlacklistFlags;
|
||||
private final DataSource dataSource;
|
||||
private final FormatEvaluator adaptiveFormatEvaluator;
|
||||
|
|
@ -100,8 +97,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
* @param manifest The initial manifest.
|
||||
* @param periodIndex The index of the period in the manifest.
|
||||
* @param adaptationSetIndex The index of the adaptation set in the period.
|
||||
* @param trackGroup The track group corresponding to the adaptation set.
|
||||
* @param tracks The indices of the selected tracks within the adaptation set.
|
||||
* @param trackSelection The track selection.
|
||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
|
||||
* @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between
|
||||
|
|
@ -109,12 +105,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
* as the server's unix time minus the local elapsed time. If unknown, set to 0.
|
||||
*/
|
||||
public DefaultDashChunkSource(Loader manifestLoader, DashManifest manifest, int periodIndex,
|
||||
int adaptationSetIndex, TrackGroup trackGroup, int[] tracks, DataSource dataSource,
|
||||
int adaptationSetIndex, TrackSelection trackSelection, DataSource dataSource,
|
||||
FormatEvaluator adaptiveFormatEvaluator, long elapsedRealtimeOffsetMs) {
|
||||
this.manifestLoader = manifestLoader;
|
||||
this.manifest = manifest;
|
||||
this.adaptationSetIndex = adaptationSetIndex;
|
||||
this.trackGroup = trackGroup;
|
||||
this.trackSelection = trackSelection;
|
||||
this.dataSource = dataSource;
|
||||
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
|
||||
this.elapsedRealtimeOffsetUs = elapsedRealtimeOffsetMs * 1000;
|
||||
|
|
@ -122,20 +118,14 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
|
||||
long periodDurationUs = getPeriodDurationUs(periodIndex);
|
||||
List<Representation> representations = getRepresentations(periodIndex);
|
||||
representationHolders = new RepresentationHolder[representations.size()];
|
||||
|
||||
for (int i = 0; i < representations.size(); i++) {
|
||||
Representation representation = representations.get(i);
|
||||
representationHolders = new RepresentationHolder[trackSelection.length];
|
||||
for (int i = 0; i < trackSelection.length; i++) {
|
||||
Representation representation = representations.get(trackSelection.getTrack(i));
|
||||
representationHolders[i] = new RepresentationHolder(periodDurationUs, representation);
|
||||
}
|
||||
enabledFormats = new Format[tracks.length];
|
||||
for (int i = 0; i < tracks.length; i++) {
|
||||
enabledFormats[i] = trackGroup.getFormat(tracks[i]);
|
||||
}
|
||||
Arrays.sort(enabledFormats, new DecreasingBandwidthComparator());
|
||||
if (adaptiveFormatEvaluator != null) {
|
||||
adaptiveFormatEvaluator.enable(enabledFormats);
|
||||
adaptiveFormatBlacklistFlags = new boolean[tracks.length];
|
||||
adaptiveFormatEvaluator.enable(trackSelection.getFormats());
|
||||
adaptiveFormatBlacklistFlags = new boolean[trackSelection.length];
|
||||
} else {
|
||||
adaptiveFormatBlacklistFlags = null;
|
||||
}
|
||||
|
|
@ -147,8 +137,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
manifest = newManifest;
|
||||
long periodDurationUs = getPeriodDurationUs(periodIndex);
|
||||
List<Representation> representations = getRepresentations(periodIndex);
|
||||
for (int i = 0; i < representationHolders.length; i++) {
|
||||
Representation representation = representations.get(i);
|
||||
for (int i = 0; i < trackSelection.length; i++) {
|
||||
Representation representation = representations.get(trackSelection.getTrack(i));
|
||||
representationHolders[i].updateRepresentation(periodDurationUs, representation);
|
||||
}
|
||||
} catch (BehindLiveWindowException e) {
|
||||
|
|
@ -167,7 +157,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
|
||||
@Override
|
||||
public int getPreferredQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
|
||||
if (fatalError != null || enabledFormats.length < 2) {
|
||||
if (fatalError != null || trackSelection.length < 2) {
|
||||
return queue.size();
|
||||
}
|
||||
return adaptiveFormatEvaluator.evaluateQueueSize(playbackPositionUs, queue,
|
||||
|
|
@ -181,12 +171,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
}
|
||||
|
||||
if (evaluation.format == null || !lastChunkWasInitialization) {
|
||||
if (enabledFormats.length > 1) {
|
||||
if (trackSelection.length > 1) {
|
||||
long bufferedDurationUs = previous != null ? (previous.endTimeUs - playbackPositionUs) : 0;
|
||||
adaptiveFormatEvaluator.evaluateFormat(bufferedDurationUs, adaptiveFormatBlacklistFlags,
|
||||
evaluation);
|
||||
} else {
|
||||
evaluation.format = enabledFormats[0];
|
||||
evaluation.format = trackSelection.getFormat(0);
|
||||
evaluation.trigger = FormatEvaluator.TRIGGER_UNKNOWN;
|
||||
evaluation.data = null;
|
||||
}
|
||||
|
|
@ -198,7 +188,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
}
|
||||
|
||||
RepresentationHolder representationHolder =
|
||||
representationHolders[getTrackIndex(selectedFormat)];
|
||||
representationHolders[trackSelection.indexOf(selectedFormat)];
|
||||
Representation selectedRepresentation = representationHolder.representation;
|
||||
DashSegmentIndex segmentIndex = representationHolder.segmentIndex;
|
||||
|
||||
|
|
@ -270,7 +260,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
if (chunk instanceof InitializationChunk) {
|
||||
InitializationChunk initializationChunk = (InitializationChunk) chunk;
|
||||
RepresentationHolder representationHolder =
|
||||
representationHolders[getTrackIndex(initializationChunk.format)];
|
||||
representationHolders[trackSelection.indexOf(initializationChunk.format)];
|
||||
Format sampleFormat = initializationChunk.getSampleFormat();
|
||||
if (sampleFormat != null) {
|
||||
representationHolder.setSampleFormat(sampleFormat);
|
||||
|
|
@ -295,7 +285,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
&& e instanceof InvalidResponseCodeException
|
||||
&& ((InvalidResponseCodeException) e).responseCode == 404) {
|
||||
RepresentationHolder representationHolder =
|
||||
representationHolders[getTrackIndex(chunk.format)];
|
||||
representationHolders[trackSelection.indexOf(chunk.format)];
|
||||
int lastAvailableSegmentNum = representationHolder.getLastSegmentNum();
|
||||
if (((MediaChunk) chunk).chunkIndex >= lastAvailableSegmentNum) {
|
||||
missingLastSegment = true;
|
||||
|
|
@ -368,16 +358,6 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
}
|
||||
}
|
||||
|
||||
private int getTrackIndex(Format format) {
|
||||
for (int i = 0; i < trackGroup.length; i++) {
|
||||
if (trackGroup.getFormat(i) == format) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Should never happen.
|
||||
throw new IllegalStateException("Invalid format: " + format);
|
||||
}
|
||||
|
||||
private long getPeriodDurationUs(int periodIndex) {
|
||||
long durationMs = manifest.getPeriodDuration(periodIndex);
|
||||
if (durationMs == -1) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
|
|||
import com.google.android.exoplayer2.extractor.ts.PtsTimestampAdjuster;
|
||||
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
|
||||
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.chunk.Chunk;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
|
||||
import com.google.android.exoplayer2.source.chunk.DataChunk;
|
||||
|
|
@ -31,6 +32,7 @@ import com.google.android.exoplayer2.source.chunk.FormatEvaluator.Evaluation;
|
|||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.Variant;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
|
||||
|
|
@ -47,7 +49,6 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
|
|
@ -78,6 +79,7 @@ public class HlsChunkSource {
|
|||
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
|
||||
private final Variant[] variants;
|
||||
private final HlsMediaPlaylist[] variantPlaylists;
|
||||
private final TrackGroup trackGroup;
|
||||
private final long[] variantLastPlaylistLoadTimesMs;
|
||||
|
||||
private boolean seenFirstExternalTrackSelection;
|
||||
|
|
@ -92,7 +94,7 @@ public class HlsChunkSource {
|
|||
private byte[] encryptionIv;
|
||||
|
||||
// Properties of enabled variants.
|
||||
private Variant[] enabledVariants;
|
||||
private TrackSelection trackSelection;
|
||||
private long[] enabledVariantBlacklistTimes;
|
||||
private boolean[] enabledVariantBlacklistFlags;
|
||||
|
||||
|
|
@ -117,11 +119,15 @@ public class HlsChunkSource {
|
|||
evaluation = new Evaluation();
|
||||
variantPlaylists = new HlsMediaPlaylist[variants.length];
|
||||
variantLastPlaylistLoadTimesMs = new long[variants.length];
|
||||
|
||||
Format[] variantFormats = new Format[variants.length];
|
||||
int[] initialTrackSelection = new int[variants.length];
|
||||
for (int i = 0; i < variants.length; i++) {
|
||||
variantFormats[i] = variants[i].format;
|
||||
initialTrackSelection[i] = i;
|
||||
}
|
||||
selectTracksInternal(initialTrackSelection, false);
|
||||
trackGroup = new TrackGroup(adaptiveFormatEvaluator != null, variantFormats);
|
||||
selectTracksInternal(new TrackSelection(trackGroup, initialTrackSelection), false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -136,19 +142,8 @@ public class HlsChunkSource {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this source supports adaptation between its tracks.
|
||||
*
|
||||
* @return Whether this source supports adaptation between its tracks.
|
||||
*/
|
||||
public boolean isAdaptive() {
|
||||
return adaptiveFormatEvaluator != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this is a live playback.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
* @return True if this is a live playback. False otherwise.
|
||||
*/
|
||||
|
|
@ -158,8 +153,6 @@ public class HlsChunkSource {
|
|||
|
||||
/**
|
||||
* Returns the duration of the source, or {@link C#UNSET_TIME_US} if the duration is unknown.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
* @return The number of tracks.
|
||||
*/
|
||||
|
|
@ -168,43 +161,25 @@ public class HlsChunkSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tracks exposed by the source.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
* Returns the track group exposed by the source.
|
||||
*
|
||||
* @return The number of tracks.
|
||||
* @return The track group.
|
||||
*/
|
||||
public int getTrackCount() {
|
||||
return variants.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format of the track at the specified index.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
* @param index The track index.
|
||||
* @return The format of the track.
|
||||
*/
|
||||
public Format getTrackFormat(int index) {
|
||||
return variants[index].format;
|
||||
public TrackGroup getTrackGroup() {
|
||||
return trackGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects tracks for use.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
* @param tracks The track indices.
|
||||
* @param trackSelection The track selection.
|
||||
*/
|
||||
public void selectTracks(int[] tracks) {
|
||||
selectTracksInternal(tracks, true);
|
||||
public void selectTracks(TrackSelection trackSelection) {
|
||||
selectTracksInternal(trackSelection, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the source.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*/
|
||||
public void reset() {
|
||||
fatalError = null;
|
||||
|
|
@ -224,10 +199,9 @@ public class HlsChunkSource {
|
|||
* @param out A holder to populate.
|
||||
*/
|
||||
public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, ChunkHolder out) {
|
||||
int previousChunkVariantIndex =
|
||||
previous != null ? getVariantIndex(previous.format) : -1;
|
||||
int previousChunkVariantIndex = previous != null ? trackGroup.indexOf(previous.format) : -1;
|
||||
updateFormatEvaluation(previous, playbackPositionUs);
|
||||
int newVariantIndex = getVariantIndex(evaluation.format);
|
||||
int newVariantIndex = trackGroup.indexOf(evaluation.format);
|
||||
boolean switchingVariant = previousChunkVariantIndex != newVariantIndex;
|
||||
HlsMediaPlaylist mediaPlaylist = variantPlaylists[newVariantIndex];
|
||||
if (mediaPlaylist == null) {
|
||||
|
|
@ -443,7 +417,7 @@ public class HlsChunkSource {
|
|||
InvalidResponseCodeException responseCodeException = (InvalidResponseCodeException) e;
|
||||
int responseCode = responseCodeException.responseCode;
|
||||
if (responseCode == 404 || responseCode == 410) {
|
||||
int enabledVariantIndex = getEnabledVariantIndex(chunk.format);
|
||||
int enabledVariantIndex = trackSelection.indexOf(chunk.format);
|
||||
boolean alreadyBlacklisted = enabledVariantBlacklistFlags[enabledVariantIndex];
|
||||
enabledVariantBlacklistFlags[enabledVariantIndex] = true;
|
||||
enabledVariantBlacklistTimes[enabledVariantIndex] = SystemClock.elapsedRealtime();
|
||||
|
|
@ -471,37 +445,21 @@ public class HlsChunkSource {
|
|||
|
||||
// Private methods.
|
||||
|
||||
private void selectTracksInternal(int[] tracks, boolean isExternal) {
|
||||
private void selectTracksInternal(TrackSelection trackSelection, boolean isExternal) {
|
||||
this.trackSelection = trackSelection;
|
||||
seenFirstExternalTrackSelection |= isExternal;
|
||||
|
||||
// Construct and sort the enabled variants.
|
||||
enabledVariants = new Variant[tracks.length];
|
||||
for (int i = 0; i < tracks.length; i++) {
|
||||
enabledVariants[i] = variants[tracks[i]];
|
||||
}
|
||||
Arrays.sort(enabledVariants, new Comparator<Variant>() {
|
||||
private final Comparator<Format> formatComparator =
|
||||
new Format.DecreasingBandwidthComparator();
|
||||
@Override
|
||||
public int compare(Variant first, Variant second) {
|
||||
return formatComparator.compare(first.format, second.format);
|
||||
}
|
||||
});
|
||||
|
||||
// Reset the enabled variant blacklist flags.
|
||||
enabledVariantBlacklistTimes = new long[enabledVariants.length];
|
||||
enabledVariantBlacklistFlags = new boolean[enabledVariants.length];
|
||||
enabledVariantBlacklistTimes = new long[trackSelection.length];
|
||||
enabledVariantBlacklistFlags = new boolean[trackSelection.length];
|
||||
|
||||
if (!isExternal) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enabledVariants.length > 1) {
|
||||
Format[] formats = new Format[enabledVariants.length];
|
||||
for (int i = 0; i < formats.length; i++) {
|
||||
formats[i] = enabledVariants[i].format;
|
||||
}
|
||||
if (trackSelection.length > 1) {
|
||||
// TODO[REFACTOR]: We need to disable this at some point.
|
||||
Format[] formats = trackSelection.getFormats();
|
||||
adaptiveFormatEvaluator.enable(formats);
|
||||
if (!Util.contains(formats, evaluation.format)) {
|
||||
evaluation.format = null;
|
||||
|
|
@ -515,23 +473,23 @@ public class HlsChunkSource {
|
|||
private void updateFormatEvaluation(HlsMediaChunk previous, long playbackPositionUs) {
|
||||
clearStaleBlacklistedVariants();
|
||||
if (!seenFirstExternalTrackSelection) {
|
||||
if (!enabledVariantBlacklistFlags[getEnabledVariantIndex(variants[0].format)]) {
|
||||
if (!enabledVariantBlacklistFlags[trackSelection.indexOf(variants[0].format)]) {
|
||||
// Use the first variant prior to external track selection, unless it's been blacklisted.
|
||||
evaluation.format = variants[0].format;
|
||||
return;
|
||||
}
|
||||
// Try from lowest bitrate to highest.
|
||||
for (int i = enabledVariants.length - 1; i >= 0; i--) {
|
||||
for (int i = trackSelection.length - 1; i >= 0; i--) {
|
||||
if (!enabledVariantBlacklistFlags[i]) {
|
||||
evaluation.format = enabledVariants[i].format;
|
||||
evaluation.format = trackSelection.getFormat(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Should never happen.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (enabledVariants.length == 1) {
|
||||
evaluation.format = enabledVariants[0].format;
|
||||
if (trackSelection.length == 1) {
|
||||
evaluation.format = trackSelection.getFormat(0);
|
||||
return;
|
||||
}
|
||||
long bufferedDurationUs;
|
||||
|
|
@ -624,26 +582,6 @@ public class HlsChunkSource {
|
|||
}
|
||||
}
|
||||
|
||||
private int getEnabledVariantIndex(Format format) {
|
||||
for (int i = 0; i < enabledVariants.length; i++) {
|
||||
if (enabledVariants[i].format == format) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Should never happen.
|
||||
throw new IllegalStateException("Invalid format: " + format);
|
||||
}
|
||||
|
||||
private int getVariantIndex(Format format) {
|
||||
for (int i = 0; i < variants.length; i++) {
|
||||
if (variants[i].format == format) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Should never happen.
|
||||
throw new IllegalStateException("Invalid format: " + format);
|
||||
}
|
||||
|
||||
// Private classes.
|
||||
|
||||
private static final class MediaPlaylistChunk extends DataChunk {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ import com.google.android.exoplayer2.util.MimeTypes;
|
|||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -429,13 +428,13 @@ public final class HlsMediaSource implements MediaPeriod, MediaSource,
|
|||
}
|
||||
// Get the subset of the new selections for the wrapper.
|
||||
ArrayList<TrackSelection> newSelections = new ArrayList<>();
|
||||
TrackGroupArray sampleStreamWrapperTrackGroups = sampleStreamWrapper.getTrackGroups();
|
||||
int[] newSelectionOriginalIndices = new int[allNewSelections.size()];
|
||||
for (int i = 0; i < allNewSelections.size(); i++) {
|
||||
TrackSelection selection = allNewSelections.get(i);
|
||||
Pair<HlsSampleStreamWrapper, Integer> sourceAndGroup = getSourceAndGroup(selection.group);
|
||||
if (sourceAndGroup.first == sampleStreamWrapper) {
|
||||
if (sampleStreamWrapperTrackGroups.indexOf(selection.group) != -1) {
|
||||
newSelectionOriginalIndices[newSelections.size()] = i;
|
||||
newSelections.add(new TrackSelection(sourceAndGroup.second, selection.getTracks()));
|
||||
newSelections.add(selection);
|
||||
}
|
||||
}
|
||||
// Do nothing if nothing has changed, except during the first selection.
|
||||
|
|
@ -452,18 +451,6 @@ public final class HlsMediaSource implements MediaPeriod, MediaSource,
|
|||
return newSelections.size() - oldStreams.size();
|
||||
}
|
||||
|
||||
private Pair<HlsSampleStreamWrapper, Integer> getSourceAndGroup(int group) {
|
||||
int totalTrackGroupCount = 0;
|
||||
for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) {
|
||||
int sourceTrackGroupCount = sampleStreamWrapper.getTrackGroups().length;
|
||||
if (group < totalTrackGroupCount + sourceTrackGroupCount) {
|
||||
return Pair.create(sampleStreamWrapper, group - totalTrackGroupCount);
|
||||
}
|
||||
totalTrackGroupCount += sourceTrackGroupCount;
|
||||
}
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
private static boolean variantHasExplicitCodecWithPrefix(Variant variant, String prefix) {
|
||||
String codecs = variant.codecs;
|
||||
if (TextUtils.isEmpty(codecs)) {
|
||||
|
|
|
|||
|
|
@ -164,11 +164,11 @@ import java.util.List;
|
|||
SampleStream[] newStreams = new SampleStream[newSelections.size()];
|
||||
for (int i = 0; i < newStreams.length; i++) {
|
||||
TrackSelection selection = newSelections.get(i);
|
||||
int group = selection.group;
|
||||
int group = trackGroups.indexOf(selection.group);
|
||||
int[] tracks = selection.getTracks();
|
||||
setTrackGroupEnabledState(group, true);
|
||||
if (group == primaryTrackGroupIndex) {
|
||||
chunkSource.selectTracks(tracks);
|
||||
chunkSource.selectTracks(new TrackSelection(chunkSource.getTrackGroup(), tracks));
|
||||
}
|
||||
newStreams[i] = new SampleStreamImpl(group);
|
||||
}
|
||||
|
|
@ -526,8 +526,8 @@ import java.util.List;
|
|||
}
|
||||
}
|
||||
|
||||
// Calculate the number of tracks that will be exposed.
|
||||
int chunkSourceTrackCount = chunkSource.getTrackCount();
|
||||
TrackGroup chunkSourceTrackGroup = chunkSource.getTrackGroup();
|
||||
int chunkSourceTrackCount = chunkSourceTrackGroup.length;
|
||||
|
||||
// Instantiate the necessary internal data-structures.
|
||||
primaryTrackGroupIndex = -1;
|
||||
|
|
@ -540,9 +540,9 @@ import java.util.List;
|
|||
if (i == primaryExtractorTrackIndex) {
|
||||
Format[] formats = new Format[chunkSourceTrackCount];
|
||||
for (int j = 0; j < chunkSourceTrackCount; j++) {
|
||||
formats[j] = getSampleFormat(chunkSource.getTrackFormat(j), sampleFormat);
|
||||
formats[j] = getSampleFormat(chunkSourceTrackGroup.getFormat(j), sampleFormat);
|
||||
}
|
||||
trackGroups[i] = new TrackGroup(chunkSource.isAdaptive(), formats);
|
||||
trackGroups[i] = new TrackGroup(chunkSourceTrackGroup.adaptive, formats);
|
||||
primaryTrackGroupIndex = i;
|
||||
} else {
|
||||
Format trackFormat = null;
|
||||
|
|
|
|||
|
|
@ -17,12 +17,10 @@ package com.google.android.exoplayer2.source.smoothstreaming;
|
|||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Format.DecreasingBandwidthComparator;
|
||||
import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
|
||||
import com.google.android.exoplayer2.extractor.mp4.Track;
|
||||
import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox;
|
||||
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.chunk.Chunk;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
|
||||
|
|
@ -32,15 +30,14 @@ import com.google.android.exoplayer2.source.chunk.FormatEvaluator.Evaluation;
|
|||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.Loader;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -61,23 +58,21 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
|
||||
@Override
|
||||
public SsChunkSource createChunkSource(Loader manifestLoader, SsManifest manifest,
|
||||
int elementIndex, TrackGroup trackGroup, int[] tracks,
|
||||
int elementIndex, TrackSelection trackSelection,
|
||||
TrackEncryptionBox[] trackEncryptionBoxes) {
|
||||
FormatEvaluator adaptiveEvaluator = tracks.length > 1
|
||||
FormatEvaluator adaptiveEvaluator = trackSelection.length > 1
|
||||
? formatEvaluatorFactory.createFormatEvaluator() : null;
|
||||
DataSource dataSource = dataSourceFactory.createDataSource();
|
||||
return new DefaultSsChunkSource(manifestLoader, manifest, elementIndex,
|
||||
trackGroup, tracks, dataSource, adaptiveEvaluator,
|
||||
trackEncryptionBoxes);
|
||||
return new DefaultSsChunkSource(manifestLoader, manifest, elementIndex, trackSelection,
|
||||
dataSource, adaptiveEvaluator, trackEncryptionBoxes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Loader manifestLoader;
|
||||
private final int elementIndex;
|
||||
private final TrackGroup trackGroup;
|
||||
private final TrackSelection trackSelection;
|
||||
private final ChunkExtractorWrapper[] extractorWrappers;
|
||||
private final Format[] enabledFormats;
|
||||
private final boolean[] adaptiveFormatBlacklistFlags;
|
||||
private final DataSource dataSource;
|
||||
private final Evaluation evaluation;
|
||||
|
|
@ -92,45 +87,40 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
* @param manifestLoader The {@link Loader} being used to load manifests.
|
||||
* @param manifest The initial manifest.
|
||||
* @param elementIndex The index of the stream element in the manifest.
|
||||
* @param trackGroup The track group corresponding to the stream element.
|
||||
* @param tracks The indices of the selected tracks within the stream element.
|
||||
* @param trackSelection The track selection.
|
||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
* @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats.
|
||||
* @param trackEncryptionBoxes Track encryption boxes for the stream.
|
||||
*/
|
||||
public DefaultSsChunkSource(Loader manifestLoader, SsManifest manifest, int elementIndex,
|
||||
TrackGroup trackGroup, int[] tracks, DataSource dataSource,
|
||||
FormatEvaluator adaptiveFormatEvaluator, TrackEncryptionBox[] trackEncryptionBoxes) {
|
||||
TrackSelection trackSelection, DataSource dataSource, FormatEvaluator adaptiveFormatEvaluator,
|
||||
TrackEncryptionBox[] trackEncryptionBoxes) {
|
||||
this.manifestLoader = manifestLoader;
|
||||
this.manifest = manifest;
|
||||
this.elementIndex = elementIndex;
|
||||
this.trackGroup = trackGroup;
|
||||
this.trackSelection = trackSelection;
|
||||
this.dataSource = dataSource;
|
||||
this.adaptiveFormatEvaluator = adaptiveFormatEvaluator;
|
||||
this.evaluation = new Evaluation();
|
||||
|
||||
StreamElement streamElement = manifest.streamElements[elementIndex];
|
||||
Format[] formats = streamElement.formats;
|
||||
extractorWrappers = new ChunkExtractorWrapper[formats.length];
|
||||
for (int j = 0; j < formats.length; j++) {
|
||||
|
||||
extractorWrappers = new ChunkExtractorWrapper[trackSelection.length];
|
||||
for (int i = 0; i < trackSelection.length; i++) {
|
||||
int manifestTrackIndex = trackSelection.getTrack(i);
|
||||
Format format = trackSelection.getFormat(i);
|
||||
int nalUnitLengthFieldLength = streamElement.type == C.TRACK_TYPE_VIDEO ? 4 : -1;
|
||||
Track track = new Track(j, streamElement.type, streamElement.timescale, C.UNSET_TIME_US,
|
||||
manifest.durationUs, formats[j], Track.TRANSFORMATION_NONE, trackEncryptionBoxes,
|
||||
nalUnitLengthFieldLength, null, null);
|
||||
Track track = new Track(manifestTrackIndex, streamElement.type, streamElement.timescale,
|
||||
C.UNSET_TIME_US, manifest.durationUs, format, Track.TRANSFORMATION_NONE,
|
||||
trackEncryptionBoxes, nalUnitLengthFieldLength, null, null);
|
||||
FragmentedMp4Extractor extractor = new FragmentedMp4Extractor(
|
||||
FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
|
||||
| FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, track);
|
||||
extractorWrappers[j] = new ChunkExtractorWrapper(extractor, formats[j], false);
|
||||
extractorWrappers[i] = new ChunkExtractorWrapper(extractor, format, false);
|
||||
}
|
||||
|
||||
enabledFormats = new Format[tracks.length];
|
||||
for (int i = 0; i < tracks.length; i++) {
|
||||
enabledFormats[i] = trackGroup.getFormat(tracks[i]);
|
||||
}
|
||||
Arrays.sort(enabledFormats, new DecreasingBandwidthComparator());
|
||||
if (adaptiveFormatEvaluator != null) {
|
||||
adaptiveFormatEvaluator.enable(enabledFormats);
|
||||
adaptiveFormatBlacklistFlags = new boolean[tracks.length];
|
||||
adaptiveFormatEvaluator.enable(trackSelection.getFormats());
|
||||
adaptiveFormatBlacklistFlags = new boolean[trackSelection.length];
|
||||
} else {
|
||||
adaptiveFormatBlacklistFlags = null;
|
||||
}
|
||||
|
|
@ -172,7 +162,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
|
||||
@Override
|
||||
public int getPreferredQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
|
||||
if (fatalError != null || enabledFormats.length < 2) {
|
||||
if (fatalError != null || trackSelection.length < 2) {
|
||||
return queue.size();
|
||||
}
|
||||
return adaptiveFormatEvaluator.evaluateQueueSize(playbackPositionUs, queue,
|
||||
|
|
@ -185,12 +175,12 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
return;
|
||||
}
|
||||
|
||||
if (enabledFormats.length > 1) {
|
||||
if (trackSelection.length > 1) {
|
||||
long bufferedDurationUs = previous != null ? (previous.endTimeUs - playbackPositionUs) : 0;
|
||||
adaptiveFormatEvaluator.evaluateFormat(bufferedDurationUs, adaptiveFormatBlacklistFlags,
|
||||
evaluation);
|
||||
} else {
|
||||
evaluation.format = enabledFormats[0];
|
||||
evaluation.format = trackSelection.getFormat(0);
|
||||
evaluation.trigger = FormatEvaluator.TRIGGER_UNKNOWN;
|
||||
evaluation.data = null;
|
||||
}
|
||||
|
|
@ -229,10 +219,10 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
long chunkEndTimeUs = chunkStartTimeUs + streamElement.getChunkDurationUs(chunkIndex);
|
||||
int currentAbsoluteChunkIndex = chunkIndex + currentManifestChunkOffset;
|
||||
|
||||
int trackGroupTrackIndex = getTrackGroupTrackIndex(trackGroup, selectedFormat);
|
||||
ChunkExtractorWrapper extractorWrapper = extractorWrappers[trackGroupTrackIndex];
|
||||
int trackSelectionIndex = trackSelection.indexOf(selectedFormat);
|
||||
ChunkExtractorWrapper extractorWrapper = extractorWrappers[trackSelectionIndex];
|
||||
|
||||
int manifestTrackIndex = getManifestTrackIndex(streamElement, selectedFormat);
|
||||
int manifestTrackIndex = trackSelection.getTrack(trackSelectionIndex);
|
||||
Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex);
|
||||
|
||||
out.chunk = newMediaChunk(selectedFormat, dataSource, uri, null, currentAbsoluteChunkIndex,
|
||||
|
|
@ -259,37 +249,6 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||
|
||||
// Private methods.
|
||||
|
||||
/**
|
||||
* Gets the index of a format in a track group, using referential equality.
|
||||
*/
|
||||
private static int getTrackGroupTrackIndex(TrackGroup trackGroup, Format format) {
|
||||
for (int i = 0; i < trackGroup.length; i++) {
|
||||
if (trackGroup.getFormat(i) == format) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Should never happen.
|
||||
throw new IllegalStateException("Invalid format: " + format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a format in an element, using format.id equality.
|
||||
* <p>
|
||||
* This method will return the same index as {@link #getTrackGroupTrackIndex(TrackGroup, Format)}
|
||||
* except in the case where a live manifest is refreshed and the ordering of the tracks in the
|
||||
* manifest has changed.
|
||||
*/
|
||||
private static int getManifestTrackIndex(StreamElement element, Format format) {
|
||||
Format[] formats = element.formats;
|
||||
for (int i = 0; i < formats.length; i++) {
|
||||
if (TextUtils.equals(formats[i].id, format.id)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Should never happen.
|
||||
throw new IllegalStateException("Invalid format: " + format);
|
||||
}
|
||||
|
||||
private static MediaChunk newMediaChunk(Format format, DataSource dataSource, Uri uri,
|
||||
String cacheKey, int chunkIndex, long chunkStartTimeUs, long chunkEndTimeUs,
|
||||
int formatEvaluatorTrigger, Object formatEvaluatorData,
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
package com.google.android.exoplayer2.source.smoothstreaming;
|
||||
|
||||
import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.chunk.ChunkSource;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.Loader;
|
||||
|
||||
/**
|
||||
|
|
@ -29,7 +29,7 @@ public interface SsChunkSource extends ChunkSource {
|
|||
interface Factory {
|
||||
|
||||
SsChunkSource createChunkSource(Loader manifestLoader, SsManifest manifest, int elementIndex,
|
||||
TrackGroup trackGroup, int[] tracks, TrackEncryptionBox[] trackEncryptionBoxes);
|
||||
TrackSelection trackSelection, TrackEncryptionBox[] trackEncryptionBoxes);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -354,11 +354,9 @@ public final class SsMediaSource implements MediaPeriod, MediaSource,
|
|||
|
||||
private ChunkSampleStream<SsChunkSource> buildSampleStream(TrackSelection selection,
|
||||
long positionUs) {
|
||||
int[] selectedTracks = selection.getTracks();
|
||||
int streamElementIndex = trackGroupElementIndices[selection.group];
|
||||
SsChunkSource chunkSource = chunkSourceFactory.createChunkSource(manifestLoader,
|
||||
manifest, streamElementIndex, trackGroups.get(selection.group), selectedTracks,
|
||||
trackEncryptionBoxes);
|
||||
int streamElementIndex = trackGroupElementIndices[trackGroups.indexOf(selection.group)];
|
||||
SsChunkSource chunkSource = chunkSourceFactory.createChunkSource(manifestLoader, manifest,
|
||||
streamElementIndex, selection, trackEncryptionBoxes);
|
||||
return new ChunkSampleStream<>(manifest.streamElements[streamElementIndex].type, chunkSource,
|
||||
this, allocator, positionUs, minLoadableRetryCount, eventDispatcher);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,17 +176,18 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
: RendererCapabilities.ADAPTIVE_SEAMLESS;
|
||||
boolean allowMixedMimeTypes = allowMixedMimeAdaptiveness
|
||||
&& (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0;
|
||||
int largestAdaptiveGroup = -1;
|
||||
TrackGroup largestAdaptiveGroup = null;
|
||||
int[] largestAdaptiveGroupTracks = NO_TRACKS;
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
int[] adaptiveTracks = getAdaptiveTracksOfGroup(trackGroups.get(i), formatSupport[i],
|
||||
TrackGroup trackGroup = trackGroups.get(i);
|
||||
int[] adaptiveTracks = getAdaptiveTracksOfGroup(trackGroup, formatSupport[i],
|
||||
allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight);
|
||||
if (adaptiveTracks.length > largestAdaptiveGroupTracks.length) {
|
||||
largestAdaptiveGroup = i;
|
||||
largestAdaptiveGroup = trackGroup;
|
||||
largestAdaptiveGroupTracks = adaptiveTracks;
|
||||
}
|
||||
}
|
||||
if (largestAdaptiveGroup != -1) {
|
||||
if (largestAdaptiveGroup != null) {
|
||||
return new TrackSelection(largestAdaptiveGroup, largestAdaptiveGroupTracks);
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +198,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||
if (isSupportedVideoTrack(trackFormatSupport[trackIndex], trackGroup.getFormat(trackIndex),
|
||||
maxVideoWidth, maxVideoHeight)) {
|
||||
return new TrackSelection(groupIndex, trackIndex);
|
||||
return new TrackSelection(trackGroup, trackIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,7 +268,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
private static TrackSelection selectSmallestSupportedVideoTrack(TrackGroupArray trackGroups,
|
||||
int[][] formatSupport) {
|
||||
int smallestPixelCount = Integer.MAX_VALUE;
|
||||
int trackGroupIndexSelection = -1;
|
||||
TrackGroup trackGroupSelection = null;
|
||||
int trackIndexSelection = -1;
|
||||
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
|
||||
TrackGroup trackGroup = trackGroups.get(groupIndex);
|
||||
|
|
@ -279,13 +280,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
&& isSupportedVideoTrack(trackFormatSupport[trackIndex], format, Integer.MAX_VALUE,
|
||||
Integer.MAX_VALUE)) {
|
||||
smallestPixelCount = pixelCount;
|
||||
trackGroupIndexSelection = groupIndex;
|
||||
trackGroupSelection = trackGroup;
|
||||
trackIndexSelection = trackIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
return trackIndexSelection != -1
|
||||
? new TrackSelection(trackGroupIndexSelection, trackIndexSelection) : null;
|
||||
return trackGroupSelection != null
|
||||
? new TrackSelection(trackGroupSelection, trackIndexSelection) : null;
|
||||
}
|
||||
|
||||
private static boolean isSupportedVideoTrack(int formatSupport, Format format, int maxVideoWidth,
|
||||
|
|
@ -305,7 +306,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||
if (isSupported(trackFormatSupport[trackIndex])
|
||||
&& formatHasLanguage(trackGroup.getFormat(trackIndex), preferredLanguage)) {
|
||||
return new TrackSelection(groupIndex, trackIndex);
|
||||
return new TrackSelection(trackGroup, trackIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -318,7 +319,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
|
||||
private static TrackSelection selectTrackForTextRenderer(TrackGroupArray trackGroups,
|
||||
int[][] formatSupport, String preferredLanguage) {
|
||||
int firstForcedGroup = -1;
|
||||
TrackGroup firstForcedGroup = null;
|
||||
int firstForcedTrack = -1;
|
||||
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
|
||||
TrackGroup trackGroup = trackGroups.get(groupIndex);
|
||||
|
|
@ -327,17 +328,17 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
if (isSupported(trackFormatSupport[trackIndex])
|
||||
&& (trackGroup.getFormat(trackIndex).selectionFlags
|
||||
& Format.SELECTION_FLAG_FORCED) != 0) {
|
||||
if (firstForcedGroup == -1) {
|
||||
firstForcedGroup = groupIndex;
|
||||
if (firstForcedGroup == null) {
|
||||
firstForcedGroup = trackGroup;
|
||||
firstForcedTrack = trackIndex;
|
||||
}
|
||||
if (formatHasLanguage(trackGroup.getFormat(trackIndex), preferredLanguage)) {
|
||||
return new TrackSelection(groupIndex, trackIndex);
|
||||
return new TrackSelection(trackGroup, trackIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return firstForcedGroup != -1 ? new TrackSelection(firstForcedGroup, firstForcedTrack) : null;
|
||||
return firstForcedGroup != null ? new TrackSelection(firstForcedGroup, firstForcedTrack) : null;
|
||||
}
|
||||
|
||||
// General track selection methods.
|
||||
|
|
@ -349,7 +350,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
int[] trackFormatSupport = formatSupport[groupIndex];
|
||||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
||||
if (isSupported(trackFormatSupport[trackIndex])) {
|
||||
return new TrackSelection(groupIndex, trackIndex);
|
||||
return new TrackSelection(trackGroup, trackIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,39 +272,26 @@ public abstract class MappingTrackSelector extends TrackSelector {
|
|||
TrackGroupArray unassociatedTrackGroupArray = new TrackGroupArray(Arrays.copyOf(
|
||||
rendererTrackGroups[rendererCapabilities.length], unassociatedTrackGroupCount));
|
||||
|
||||
TrackSelection[] rendererTrackSelections = selectTracks(rendererCapabilities,
|
||||
rendererTrackGroupArrays, rendererFormatSupports);
|
||||
TrackSelection[] trackSelections = selectTracks(rendererCapabilities, rendererTrackGroupArrays,
|
||||
rendererFormatSupports);
|
||||
|
||||
// Apply track disabling and overriding.
|
||||
for (int i = 0; i < rendererCapabilities.length; i++) {
|
||||
if (rendererDisabledFlags.get(i)) {
|
||||
rendererTrackSelections[i] = null;
|
||||
trackSelections[i] = null;
|
||||
} else {
|
||||
Map<TrackGroupArray, TrackSelection> override = trackSelectionOverrides.get(i);
|
||||
TrackSelection overrideSelection = override == null ? null
|
||||
: override.get(rendererTrackGroupArrays[i]);
|
||||
if (overrideSelection != null) {
|
||||
rendererTrackSelections[i] = overrideSelection;
|
||||
trackSelections[i] = overrideSelection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The track selections above index into the track group arrays associated to each renderer,
|
||||
// and not to the original track groups passed to this method. Build the corresponding track
|
||||
// selections into the original track groups to pass back as the final selection.
|
||||
TrackSelection[] trackSelections = new TrackSelection[rendererCapabilities.length];
|
||||
for (int i = 0; i < rendererCapabilities.length; i++) {
|
||||
TrackSelection selection = rendererTrackSelections[i];
|
||||
if (selection != null) {
|
||||
TrackGroup group = rendererTrackGroupArrays[i].get(selection.group);
|
||||
int originalGroupIndex = findGroupInGroupArray(trackGroups, group);
|
||||
trackSelections[i] = new TrackSelection(originalGroupIndex, selection.getTracks());
|
||||
}
|
||||
}
|
||||
|
||||
// Package up the track information and selections.
|
||||
TrackSelectionArray trackSelectionArray = new TrackSelectionArray(trackSelections);
|
||||
TrackInfo trackInfo = new TrackInfo(rendererTrackGroupArrays, rendererTrackSelections,
|
||||
TrackInfo trackInfo = new TrackInfo(rendererTrackGroupArrays, trackSelections,
|
||||
mixedMimeTypeAdaptationSupport, rendererFormatSupports, unassociatedTrackGroupArray);
|
||||
return Pair.<TrackSelectionArray, Object>create(trackSelectionArray, trackInfo);
|
||||
}
|
||||
|
|
@ -403,23 +390,6 @@ public abstract class MappingTrackSelector extends TrackSelector {
|
|||
return mixedMimeTypeAdaptationSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified group in a group array, using referential equality.
|
||||
*
|
||||
* @param groupArray The group array to search.
|
||||
* @param group The group to search for.
|
||||
* @return The index of the group in the group array.
|
||||
* @throws IllegalStateException If the group was not found.
|
||||
*/
|
||||
private static int findGroupInGroupArray(TrackGroupArray groupArray, TrackGroup group) {
|
||||
for (int i = 0; i < groupArray.length; i++) {
|
||||
if (groupArray.get(i) == group) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
private void notifyTrackInfoChanged(final TrackInfo trackInfo) {
|
||||
if (eventHandler != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
|
|
|
|||
|
|
@ -15,44 +15,93 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.trackselection;
|
||||
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Format.DecreasingBandwidthComparator;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Defines a track selection.
|
||||
* A track selection, consisting of a {@link TrackGroup} and a selected subset of the tracks within
|
||||
* it. The selected tracks are exposed in order of decreasing bandwidth.
|
||||
*/
|
||||
public final class TrackSelection {
|
||||
|
||||
/**
|
||||
* The index of the selected {@link TrackGroup}.
|
||||
* The selected {@link TrackGroup}.
|
||||
*/
|
||||
public final int group;
|
||||
public final TrackGroup group;
|
||||
/**
|
||||
* The number of selected tracks within the {@link TrackGroup}. Always greater than zero.
|
||||
*/
|
||||
public final int length;
|
||||
|
||||
private final int[] tracks;
|
||||
private final Format[] formats;
|
||||
|
||||
// Lazily initialized hashcode.
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* @param group The index of the {@link TrackGroup}.
|
||||
* @param group The {@link TrackGroup}. Must not be null.
|
||||
* @param tracks The indices of the selected tracks within the {@link TrackGroup}. Must not be
|
||||
* null or empty.
|
||||
* null or empty. May be in any order.
|
||||
*/
|
||||
public TrackSelection(int group, int... tracks) {
|
||||
public TrackSelection(TrackGroup group, int... tracks) {
|
||||
Assertions.checkState(tracks.length > 0);
|
||||
this.group = group;
|
||||
this.tracks = tracks;
|
||||
this.group = Assertions.checkNotNull(group);
|
||||
this.length = tracks.length;
|
||||
// Set the formats, sorted in order of decreasing bandwidth.
|
||||
formats = new Format[length];
|
||||
for (int i = 0; i < tracks.length; i++) {
|
||||
formats[i] = group.getFormat(tracks[i]);
|
||||
}
|
||||
Arrays.sort(formats, new DecreasingBandwidthComparator());
|
||||
// Set the format indices in the same order.
|
||||
this.tracks = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
this.tracks[i] = group.indexOf(formats[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the selected track at a given index in the selection.
|
||||
* Gets the format of the track at a given index in the selection.
|
||||
*
|
||||
* @param index The index in the selection.
|
||||
* @return The format of the selected track.
|
||||
*/
|
||||
public Format getFormat(int index) {
|
||||
return formats[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the formats of the selected tracks.
|
||||
*
|
||||
* @return The track formats.
|
||||
*/
|
||||
public Format[] getFormats() {
|
||||
return formats.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index in the selection of the track with the specified format.
|
||||
*
|
||||
* @param format The format.
|
||||
* @return The index in the selection, or -1 if the track with the specified format is not part of
|
||||
* the selection.
|
||||
*/
|
||||
public int indexOf(Format format) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (formats[i] == format) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index in the track group of the track at a given index in the selection.
|
||||
*
|
||||
* @param index The index in the selection.
|
||||
* @return The index of the selected track.
|
||||
|
|
@ -62,7 +111,7 @@ public final class TrackSelection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the individual track indices.
|
||||
* Gets a copy of the selected tracks in the track group.
|
||||
*
|
||||
* @return The track indices.
|
||||
*/
|
||||
|
|
@ -71,24 +120,25 @@ public final class TrackSelection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets whether a given track index is included in the selection.
|
||||
* Gets the index in the selection of the track with the specified index in the track group.
|
||||
*
|
||||
* @param trackIndex The track index.
|
||||
* @return True if the index is included in the selection. False otherwise.
|
||||
* @param trackIndex The index in the track group.
|
||||
* @return The index in the selection, or -1 if the track with the specified index is not part of
|
||||
* the selection.
|
||||
*/
|
||||
public boolean containsTrack(int trackIndex) {
|
||||
public int indexOf(int trackIndex) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (tracks[i] == trackIndex) {
|
||||
return true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
hashCode = 31 * group + Arrays.hashCode(tracks);
|
||||
hashCode = 31 * System.identityHashCode(group) + Arrays.hashCode(tracks);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -826,11 +826,13 @@ public final class DashTest extends ActivityInstrumentationTestCase2<HostActivit
|
|||
Assertions.checkState(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].length == 1);
|
||||
Assertions.checkState(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].length == 1);
|
||||
TrackSelection[] selections = new TrackSelection[rendererCapabilities.length];
|
||||
selections[VIDEO_RENDERER_INDEX] = new TrackSelection(0,
|
||||
selections[VIDEO_RENDERER_INDEX] = new TrackSelection(
|
||||
rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0),
|
||||
getTrackIndices(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0),
|
||||
rendererFormatSupports[VIDEO_RENDERER_INDEX][0], videoFormatIds,
|
||||
canIncludeAdditionalVideoFormats));
|
||||
selections[AUDIO_RENDERER_INDEX] = new TrackSelection(0,
|
||||
selections[AUDIO_RENDERER_INDEX] = new TrackSelection(
|
||||
rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0),
|
||||
getTrackIndices(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0),
|
||||
rendererFormatSupports[AUDIO_RENDERER_INDEX][0], audioFormatIds, false));
|
||||
includedAdditionalVideoFormats =
|
||||
|
|
|
|||
Loading…
Reference in a new issue