Introduce TrackGroupArray.

This change replaces TrackGroup[] with TrackGroupArray. This is
to allow equality based hashCode and equals implementations.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=115563146
This commit is contained in:
olly 2016-02-25 08:06:53 -08:00 committed by Oliver Woodman
parent d1cb4bc1af
commit 73400907fc
13 changed files with 175 additions and 55 deletions

View File

@ -299,13 +299,13 @@ import java.util.concurrent.atomic.AtomicInteger;
durationUs = source.getDurationUs(); durationUs = source.getDurationUs();
bufferedPositionUs = source.getBufferedPositionUs(); bufferedPositionUs = source.getBufferedPositionUs();
TrackGroup[] trackGroups = source.getTrackGroups(); TrackGroupArray trackGroups = source.getTrackGroups();
// The maximum number of tracks that one renderer can support is the total number of tracks in // The maximum number of tracks that one renderer can support is the total number of tracks in
// all groups, plus possibly one adaptive track per group. // all groups, plus possibly one adaptive track per group.
int maxTrackCount = trackGroups.length; int maxTrackCount = trackGroups.length;
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
maxTrackCount += trackGroups[groupIndex].length; maxTrackCount += trackGroups.get(groupIndex).length;
} }
// Construct tracks for each renderer. // Construct tracks for each renderer.
Format[][] externalTrackFormats = new Format[renderers.length][]; Format[][] externalTrackFormats = new Format[renderers.length][];
@ -316,7 +316,7 @@ import java.util.concurrent.atomic.AtomicInteger;
TrackSelection[] rendererTrackSelections = new TrackSelection[maxTrackCount]; TrackSelection[] rendererTrackSelections = new TrackSelection[maxTrackCount];
Format[][] rendererTrackFormats = new Format[maxTrackCount][]; Format[][] rendererTrackFormats = new Format[maxTrackCount][];
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup trackGroup = trackGroups[groupIndex]; TrackGroup trackGroup = trackGroups.get(groupIndex);
// TODO[REFACTOR]: This should check that the renderer is capable of adaptive playback, in // TODO[REFACTOR]: This should check that the renderer is capable of adaptive playback, in
// addition to checking that the group is adaptive. // addition to checking that the group is adaptive.
if (trackGroup.adaptive) { if (trackGroup.adaptive) {

View File

@ -143,7 +143,7 @@ public final class Format {
*/ */
public final String language; public final String language;
// Lazy-initialized hashcode and framework media format. // Lazily initialized hashcode and framework media format.
private int hashCode; private int hashCode;
private MediaFormat frameworkMediaFormat; private MediaFormat frameworkMediaFormat;

View File

@ -75,7 +75,7 @@ public final class FrameworkSampleSource implements SampleSource {
private boolean prepared; private boolean prepared;
private long durationUs; private long durationUs;
private MediaExtractor extractor; private MediaExtractor extractor;
private TrackGroup[] tracks; private TrackGroupArray tracks;
private int[] trackStates; private int[] trackStates;
private boolean[] pendingResets; private boolean[] pendingResets;
@ -133,7 +133,7 @@ public final class FrameworkSampleSource implements SampleSource {
durationUs = C.UNKNOWN_TIME_US; durationUs = C.UNKNOWN_TIME_US;
trackStates = new int[extractor.getTrackCount()]; trackStates = new int[extractor.getTrackCount()];
pendingResets = new boolean[trackStates.length]; pendingResets = new boolean[trackStates.length];
tracks = new TrackGroup[trackStates.length]; TrackGroup[] tracks = new TrackGroup[trackStates.length];
for (int i = 0; i < trackStates.length; i++) { for (int i = 0; i < trackStates.length; i++) {
MediaFormat format = extractor.getTrackFormat(i); MediaFormat format = extractor.getTrackFormat(i);
if (format.containsKey(MediaFormat.KEY_DURATION)) { if (format.containsKey(MediaFormat.KEY_DURATION)) {
@ -141,6 +141,7 @@ public final class FrameworkSampleSource implements SampleSource {
} }
tracks[i] = new TrackGroup(createFormat(i, format)); tracks[i] = new TrackGroup(createFormat(i, format));
} }
this.tracks = new TrackGroupArray(tracks);
prepared = true; prepared = true;
return true; return true;
} }
@ -156,7 +157,7 @@ public final class FrameworkSampleSource implements SampleSource {
} }
@Override @Override
public TrackGroup[] getTrackGroups() { public TrackGroupArray getTrackGroups() {
return tracks; return tracks;
} }
@ -194,7 +195,7 @@ public final class FrameworkSampleSource implements SampleSource {
return TrackStream.NOTHING_READ; return TrackStream.NOTHING_READ;
} }
if (trackStates[track] != TRACK_STATE_FORMAT_SENT) { if (trackStates[track] != TRACK_STATE_FORMAT_SENT) {
formatHolder.format = tracks[track].getFormat(0); formatHolder.format = tracks.get(track).getFormat(0);
formatHolder.drmInitData = Util.SDK_INT >= 18 ? getDrmInitDataV18() : null; formatHolder.drmInitData = Util.SDK_INT >= 18 ? getDrmInitDataV18() : null;
trackStates[track] = TRACK_STATE_FORMAT_SENT; trackStates[track] = TRACK_STATE_FORMAT_SENT;
return TrackStream.FORMAT_READ; return TrackStream.FORMAT_READ;

View File

@ -28,7 +28,7 @@ public class MultiSampleSource implements SampleSource {
private boolean prepared; private boolean prepared;
private long durationUs; private long durationUs;
private TrackGroup[] tracks; private TrackGroupArray trackGroups;
public MultiSampleSource(SampleSource... sources) { public MultiSampleSource(SampleSource... sources) {
this.sources = sources; this.sources = sources;
@ -53,14 +53,15 @@ public class MultiSampleSource implements SampleSource {
durationUs = sources[i].getDurationUs(); durationUs = sources[i].getDurationUs();
} }
} }
tracks = new TrackGroup[totalTrackGroupCount]; TrackGroup[] trackGroups = new TrackGroup[totalTrackGroupCount];
int trackGroupIndex = 0; int trackGroupIndex = 0;
for (int i = 0; i < sources.length; i++) { for (int i = 0; i < sources.length; i++) {
int sourceTrackGroupCount = sources[i].getTrackGroups().length; int sourceTrackGroupCount = sources[i].getTrackGroups().length;
for (int j = 0; j < sourceTrackGroupCount; j++) { for (int j = 0; j < sourceTrackGroupCount; j++) {
tracks[trackGroupIndex++] = sources[i].getTrackGroups()[j]; trackGroups[trackGroupIndex++] = sources[i].getTrackGroups().get(j);
} }
} }
this.trackGroups = new TrackGroupArray(trackGroups);
} }
return prepared; return prepared;
} }
@ -71,8 +72,8 @@ public class MultiSampleSource implements SampleSource {
} }
@Override @Override
public TrackGroup[] getTrackGroups() { public TrackGroupArray getTrackGroups() {
return tracks; return trackGroups;
} }
@Override @Override

View File

@ -58,7 +58,7 @@ public interface SampleSource {
* *
* @return The {@link TrackGroup}s. * @return The {@link TrackGroup}s.
*/ */
public TrackGroup[] getTrackGroups(); public TrackGroupArray getTrackGroups();
/** /**
* Indicates to the source that it should continue buffering data for its enabled tracks. * Indicates to the source that it should continue buffering data for its enabled tracks.

View File

@ -52,7 +52,7 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load
private final Format format; private final Format format;
private final long durationUs; private final long durationUs;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final TrackGroup[] trackGroups; private final TrackGroupArray tracks;
private int state; private int state;
private byte[] sampleData; private byte[] sampleData;
@ -75,7 +75,7 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load
this.format = format; this.format = format;
this.durationUs = durationUs; this.durationUs = durationUs;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
trackGroups = new TrackGroup[] {new TrackGroup(format)}; tracks = new TrackGroupArray(new TrackGroup(format));
sampleData = new byte[INITIAL_SAMPLE_SIZE]; sampleData = new byte[INITIAL_SAMPLE_SIZE];
} }
@ -105,8 +105,8 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load
} }
@Override @Override
public TrackGroup[] getTrackGroups() { public TrackGroupArray getTrackGroups() {
return trackGroups; return tracks;
} }
@Override @Override

View File

@ -15,6 +15,10 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.util.Assertions;
import java.util.Arrays;
/** /**
* Defines a group of tracks exposed by a {@link SampleSource}. * Defines a group of tracks exposed by a {@link SampleSource}.
* <p> * <p>
@ -26,7 +30,7 @@ package com.google.android.exoplayer;
public final class TrackGroup { public final class TrackGroup {
/** /**
* The number of tracks in the group. * The number of tracks in the group. Always greater than zero.
*/ */
public final int length; public final int length;
/** /**
@ -36,18 +40,22 @@ public final class TrackGroup {
private final Format[] formats; private final Format[] formats;
// Lazily initialized hashcode.
private int hashCode;
/** /**
* @param format The format of the single track. * @param format The format of the single track.
*/ */
public TrackGroup(Format format) { public TrackGroup(Format format) {
this(false, format); this(false, Assertions.checkNotNull(format));
} }
/** /**
* @param adaptive Whether it's possible to adapt between multiple tracks in the group. * @param adaptive Whether it's possible to adapt between multiple tracks in the group.
* @param formats The track formats. * @param formats The track formats. Must not be null or empty. Must not contain null elements.
*/ */
public TrackGroup(boolean adaptive, Format... formats) { public TrackGroup(boolean adaptive, Format... formats) {
Assertions.checkState(formats.length > 0);
this.adaptive = adaptive; this.adaptive = adaptive;
this.formats = formats; this.formats = formats;
length = formats.length; length = formats.length;
@ -63,4 +71,29 @@ public final class TrackGroup {
return formats[index]; return formats[index];
} }
@Override
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 31 * result + (adaptive ? 1231 : 1237);
result = 31 * result + Arrays.hashCode(formats);
result = 31 * result + length;
hashCode = result;
}
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackGroup other = (TrackGroup) obj;
return adaptive == other.adaptive && length == other.length
&& Arrays.equals(formats, other.formats);
}
} }

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer;
import java.util.Arrays;
/**
* An array of {@link TrackGroup}s exposed by a {@link SampleSource}.
*/
public final class TrackGroupArray {
/**
* The number of groups in the list. Greater than or equal to zero.
*/
public final int length;
private final TrackGroup[] trackGroups;
// Lazily initialized hashcode.
private int hashCode;
/**
* @param trackGroups The groups. Must not be null or contain null elements, but may be empty.
*/
public TrackGroupArray(TrackGroup... trackGroups) {
this.trackGroups = trackGroups;
this.length = trackGroups.length;
}
/**
* Gets the group at a given index.
*
* @param index The index of the group.
* @return The group.
*/
public TrackGroup get(int index) {
return trackGroups[index];
}
@Override
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 31 * result + Arrays.hashCode(trackGroups);
result = 31 * result + length;
hashCode = result;
}
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackGroupArray other = (TrackGroupArray) obj;
return length == other.length && Arrays.equals(trackGroups, other.trackGroups);
}
}

View File

@ -22,6 +22,7 @@ import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.extractor.DefaultTrackOutput;
@ -78,7 +79,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
private long lastPerformedBufferOperation; private long lastPerformedBufferOperation;
private boolean pendingReset; private boolean pendingReset;
private TrackGroup[] trackGroups; private TrackGroupArray trackGroups;
private long durationUs; private long durationUs;
private Loader loader; private Loader loader;
private boolean loadingFinished; private boolean loadingFinished;
@ -156,11 +157,11 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
} }
durationUs = chunkSource.getDurationUs(); durationUs = chunkSource.getDurationUs();
TrackGroup trackGroup = chunkSource.getTracks(); TrackGroup trackGroup = chunkSource.getTracks();
if (trackGroup.length > 0) { if (trackGroup != null) {
loader = new Loader("Loader:" + trackGroup.getFormat(0).containerMimeType); loader = new Loader("Loader:" + trackGroup.getFormat(0).containerMimeType);
trackGroups = new TrackGroup[] {trackGroup}; trackGroups = new TrackGroupArray(trackGroup);
} else { } else {
trackGroups = new TrackGroup[0]; trackGroups = new TrackGroupArray();
} }
state = STATE_PREPARED; state = STATE_PREPARED;
return true; return true;
@ -177,7 +178,7 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
} }
@Override @Override
public TrackGroup[] getTrackGroups() { public TrackGroupArray getTrackGroups() {
Assertions.checkState(state != STATE_IDLE); Assertions.checkState(state != STATE_IDLE);
return trackGroups; return trackGroups;
} }

View File

@ -495,9 +495,10 @@ public class DashChunkSource implements ChunkSource {
for (int i = 0; i < period.adaptationSets.size(); i++) { for (int i = 0; i < period.adaptationSets.size(); i++) {
AdaptationSet adaptationSet = period.adaptationSets.get(i); AdaptationSet adaptationSet = period.adaptationSets.get(i);
if (adaptationSet.type == adaptationSetType) { if (adaptationSet.type == adaptationSetType) {
// We've found an adaptation set of the exposed type.
adaptationSetIndex = i; adaptationSetIndex = i;
List<Representation> representations = adaptationSet.representations; List<Representation> representations = adaptationSet.representations;
if (!representations.isEmpty()) {
// We've found a non-empty adaptation set of the exposed type.
Format[] trackFormats = new Format[representations.size()]; Format[] trackFormats = new Format[representations.size()];
for (int j = 0; j < trackFormats.length; j++) { for (int j = 0; j < trackFormats.length; j++) {
trackFormats[j] = representations.get(j).format; trackFormats[j] = representations.get(j).format;
@ -506,7 +507,8 @@ public class DashChunkSource implements ChunkSource {
return; return;
} }
} }
trackGroup = new TrackGroup(adaptiveFormatEvaluator != null); }
trackGroup = null;
} }
// Visible for testing. // Visible for testing.

View File

@ -21,6 +21,7 @@ import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
@ -176,7 +177,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private boolean prepared; private boolean prepared;
private int enabledTrackCount; private int enabledTrackCount;
private TrackGroup[] tracks; private TrackGroupArray tracks;
private long durationUs; private long durationUs;
private boolean[] pendingMediaFormat; private boolean[] pendingMediaFormat;
private boolean[] pendingResets; private boolean[] pendingResets;
@ -264,7 +265,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
if (seekMap != null && tracksBuilt && haveFormatsForAllTracks()) { if (seekMap != null && tracksBuilt && haveFormatsForAllTracks()) {
int trackCount = sampleQueues.size(); int trackCount = sampleQueues.size();
tracks = new TrackGroup[trackCount]; TrackGroup[] tracks = new TrackGroup[trackCount];
trackEnabledStates = new boolean[trackCount]; trackEnabledStates = new boolean[trackCount];
pendingResets = new boolean[trackCount]; pendingResets = new boolean[trackCount];
pendingMediaFormat = new boolean[trackCount]; pendingMediaFormat = new boolean[trackCount];
@ -272,6 +273,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
for (int i = 0; i < trackCount; i++) { for (int i = 0; i < trackCount; i++) {
tracks[i] = new TrackGroup(sampleQueues.valueAt(i).getFormat()); tracks[i] = new TrackGroup(sampleQueues.valueAt(i).getFormat());
} }
this.tracks = new TrackGroupArray(tracks);
prepared = true; prepared = true;
return true; return true;
} }
@ -290,7 +292,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
} }
@Override @Override
public TrackGroup[] getTrackGroups() { public TrackGroupArray getTrackGroups() {
return tracks; return tracks;
} }

View File

@ -22,6 +22,7 @@ import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener; import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
@ -79,7 +80,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
// Tracks are complicated in HLS. See documentation of buildTracks for details. // Tracks are complicated in HLS. See documentation of buildTracks for details.
// Indexed by track (as exposed by this source). // Indexed by track (as exposed by this source).
private TrackGroup[] trackGroups; private TrackGroupArray trackGroups;
private int primaryTrackGroupIndex; private int primaryTrackGroupIndex;
private int[] primarySelectedTracks; private int[] primarySelectedTracks;
// Indexed by group. // Indexed by group.
@ -181,7 +182,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
} }
@Override @Override
public TrackGroup[] getTrackGroups() { public TrackGroupArray getTrackGroups() {
Assertions.checkState(prepared); Assertions.checkState(prepared);
return trackGroups; return trackGroups;
} }
@ -506,12 +507,12 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
// Instantiate the necessary internal data-structures. // Instantiate the necessary internal data-structures.
primaryTrackGroupIndex = -1; primaryTrackGroupIndex = -1;
trackGroups = new TrackGroup[extractorTrackCount];
groupEnabledStates = new boolean[extractorTrackCount]; groupEnabledStates = new boolean[extractorTrackCount];
pendingResets = new boolean[extractorTrackCount]; pendingResets = new boolean[extractorTrackCount];
downstreamSampleFormats = new Format[extractorTrackCount]; downstreamSampleFormats = new Format[extractorTrackCount];
// Construct the set of exposed track groups. // Construct the set of exposed track groups.
TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
for (int i = 0; i < extractorTrackCount; i++) { for (int i = 0; i < extractorTrackCount; i++) {
Format sampleFormat = extractor.getSampleFormat(i); Format sampleFormat = extractor.getSampleFormat(i);
if (i == primaryExtractorTrackIndex) { if (i == primaryExtractorTrackIndex) {
@ -525,6 +526,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
trackGroups[i] = new TrackGroup(sampleFormat); trackGroups[i] = new TrackGroup(sampleFormat);
} }
} }
this.trackGroups = new TrackGroupArray(trackGroups);
} }
/** /**

View File

@ -328,10 +328,10 @@ public class SmoothStreamingChunkSource implements ChunkSource {
private void initForManifest(SmoothStreamingManifest manifest) { private void initForManifest(SmoothStreamingManifest manifest) {
for (int i = 0; i < manifest.streamElements.length; i++) { for (int i = 0; i < manifest.streamElements.length; i++) {
if (manifest.streamElements[i].type == streamElementType) { if (manifest.streamElements[i].type == streamElementType) {
// We've found an element of the desired type.
elementIndex = i;
long timescale = manifest.streamElements[i].timescale;
Format[] formats = manifest.streamElements[i].formats; Format[] formats = manifest.streamElements[i].formats;
if (formats.length > 0) {
// We've found an element of the desired type.
long timescale = manifest.streamElements[i].timescale;
extractorWrappers = new ChunkExtractorWrapper[formats.length]; extractorWrappers = new ChunkExtractorWrapper[formats.length];
for (int j = 0; j < formats.length; j++) { for (int j = 0; j < formats.length; j++) {
int nalUnitLengthFieldLength = streamElementType == StreamElement.TYPE_VIDEO ? 4 : -1; int nalUnitLengthFieldLength = streamElementType == StreamElement.TYPE_VIDEO ? 4 : -1;
@ -343,12 +343,14 @@ public class SmoothStreamingChunkSource implements ChunkSource {
extractor.setTrack(track); extractor.setTrack(track);
extractorWrappers[j] = new ChunkExtractorWrapper(extractor); extractorWrappers[j] = new ChunkExtractorWrapper(extractor);
} }
elementIndex = i;
trackGroup = new TrackGroup(adaptiveFormatEvaluator != null, formats); trackGroup = new TrackGroup(adaptiveFormatEvaluator != null, formats);
return; return;
} }
} }
}
extractorWrappers = null; extractorWrappers = null;
trackGroup = new TrackGroup(adaptiveFormatEvaluator != null); trackGroup = null;
} }
/** /**