Make SampleSource interfaces right for TrackSelector.

The TrackSelector API will look like:

TrackSelection[] selectTrack(
    TrackRenderer[] renderers, TrackGroup[] trackGroups);

In this CL:

- SampleSources return TrackGroup[], so that the result can be easily
  passed to the selector.
- TrackStream gets its own file to sit alongside other Track* classes.
- A TrackSelection object is introduced to encapsulate group and track
  indices.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=115251331
This commit is contained in:
olly 2016-02-22 11:32:40 -08:00 committed by Oliver Woodman
parent 39a924451a
commit 6ba4fa3b51
19 changed files with 232 additions and 207 deletions

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent; import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.PriorityHandlerThread; import com.google.android.exoplayer.util.PriorityHandlerThread;
import com.google.android.exoplayer.util.TraceUtil; import com.google.android.exoplayer.util.TraceUtil;
@ -75,8 +74,7 @@ import java.util.concurrent.atomic.AtomicInteger;
private final long minRebufferUs; private final long minRebufferUs;
private final List<TrackRenderer> enabledRenderers; private final List<TrackRenderer> enabledRenderers;
private final int[] selectedTrackIndices; private final int[] selectedTrackIndices;
private final int[][] groupIndices; private final TrackSelection[][] trackSelections;
private final int[][][] trackIndices;
private final Format[][][] trackFormats; private final Format[][][] trackFormats;
private final Handler handler; private final Handler handler;
private final HandlerThread internalPlaybackThread; private final HandlerThread internalPlaybackThread;
@ -127,9 +125,8 @@ import java.util.concurrent.atomic.AtomicInteger;
standaloneMediaClock = new StandaloneMediaClock(); standaloneMediaClock = new StandaloneMediaClock();
pendingSeekCount = new AtomicInteger(); pendingSeekCount = new AtomicInteger();
enabledRenderers = new ArrayList<>(renderers.length); enabledRenderers = new ArrayList<>(renderers.length);
groupIndices = new int[renderers.length][]; trackSelections = new TrackSelection[renderers.length][];
trackFormats = new Format[renderers.length][][]; trackFormats = new Format[renderers.length][][];
trackIndices = new int[renderers.length][][];
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can // Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
// not normally change to this priority" is incorrect. // not normally change to this priority" is incorrect.
internalPlaybackThread = new PriorityHandlerThread("ExoPlayerImplInternal:Handler", internalPlaybackThread = new PriorityHandlerThread("ExoPlayerImplInternal:Handler",
@ -300,17 +297,15 @@ import java.util.concurrent.atomic.AtomicInteger;
return; return;
} }
this.durationUs = source.getDurationUs(); durationUs = source.getDurationUs();
this.bufferedPositionUs = source.getBufferedPositionUs(); bufferedPositionUs = source.getBufferedPositionUs();
TrackGroup[] trackGroups = source.getTrackGroups();
boolean allRenderersEnded = true;
boolean allRenderersReadyOrEnded = true;
// 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 = source.getTrackGroupCount(); int maxTrackCount = trackGroups.length;
for (int groupIndex = 0; groupIndex < source.getTrackGroupCount(); groupIndex++) { for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
maxTrackCount += source.getTrackGroup(groupIndex).length; maxTrackCount += trackGroups[groupIndex].length;
} }
// Construct tracks for each renderer. // Construct tracks for each renderer.
Format[][] externalTrackFormats = new Format[renderers.length][]; Format[][] externalTrackFormats = new Format[renderers.length][];
@ -318,11 +313,10 @@ import java.util.concurrent.atomic.AtomicInteger;
TrackRenderer renderer = renderers[rendererIndex]; TrackRenderer renderer = renderers[rendererIndex];
int rendererTrackCount = 0; int rendererTrackCount = 0;
Format[] rendererExternalTrackFormats = new Format[maxTrackCount]; Format[] rendererExternalTrackFormats = new Format[maxTrackCount];
int[] rendererTrackGroups = new int[maxTrackCount]; TrackSelection[] rendererTrackSelections = new TrackSelection[maxTrackCount];
int[][] rendererTrackIndices = new int[maxTrackCount][];
Format[][] rendererTrackFormats = new Format[maxTrackCount][]; Format[][] rendererTrackFormats = new Format[maxTrackCount][];
for (int groupIndex = 0; groupIndex < source.getTrackGroupCount(); groupIndex++) { for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup trackGroup = source.getTrackGroup(groupIndex); TrackGroup trackGroup = trackGroups[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) {
@ -331,7 +325,7 @@ import java.util.concurrent.atomic.AtomicInteger;
int[] adaptiveTrackIndices = new int[trackGroup.length]; int[] adaptiveTrackIndices = new int[trackGroup.length];
Format[] adaptiveTrackFormats = new Format[trackGroup.length]; Format[] adaptiveTrackFormats = new Format[trackGroup.length];
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
Format trackFormat = source.getTrackGroup(groupIndex).getFormat(trackIndex); Format trackFormat = trackGroup.getFormat(trackIndex);
if ((renderer.supportsFormat(trackFormat) & TrackRenderer.FORMAT_SUPPORT_MASK) if ((renderer.supportsFormat(trackFormat) & TrackRenderer.FORMAT_SUPPORT_MASK)
== TrackRenderer.FORMAT_HANDLED) { == TrackRenderer.FORMAT_HANDLED) {
adaptiveTrackIndices[adaptiveTrackIndexCount] = trackIndex; adaptiveTrackIndices[adaptiveTrackIndexCount] = trackIndex;
@ -340,9 +334,8 @@ import java.util.concurrent.atomic.AtomicInteger;
} }
if (adaptiveTrackIndexCount > 1) { if (adaptiveTrackIndexCount > 1) {
// We succeeded in building an adaptive track. // We succeeded in building an adaptive track.
rendererTrackGroups[rendererTrackCount] = groupIndex; rendererTrackSelections[rendererTrackCount] = new TrackSelection(groupIndex,
rendererTrackIndices[rendererTrackCount] = Arrays.copyOf(adaptiveTrackIndices, adaptiveTrackIndexCount));
Arrays.copyOf(adaptiveTrackIndices, adaptiveTrackIndexCount);
rendererTrackFormats[rendererTrackCount] = rendererTrackFormats[rendererTrackCount] =
Arrays.copyOf(adaptiveTrackFormats, adaptiveTrackIndexCount); Arrays.copyOf(adaptiveTrackFormats, adaptiveTrackIndexCount);
rendererExternalTrackFormats[rendererTrackCount++] = Format.createSampleFormat( rendererExternalTrackFormats[rendererTrackCount++] = Format.createSampleFormat(
@ -350,30 +343,32 @@ import java.util.concurrent.atomic.AtomicInteger;
} }
} }
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
Format trackFormat = source.getTrackGroup(groupIndex).getFormat(trackIndex); Format trackFormat = trackGroup.getFormat(trackIndex);
if ((renderer.supportsFormat(trackFormat) & TrackRenderer.FORMAT_SUPPORT_MASK) if ((renderer.supportsFormat(trackFormat) & TrackRenderer.FORMAT_SUPPORT_MASK)
== TrackRenderer.FORMAT_HANDLED) { == TrackRenderer.FORMAT_HANDLED) {
rendererTrackGroups[rendererTrackCount] = groupIndex; rendererTrackSelections[rendererTrackCount] = new TrackSelection(groupIndex,
rendererTrackIndices[rendererTrackCount] = new int[] {trackIndex}; trackIndex);
rendererTrackFormats[rendererTrackCount] = new Format[] {trackFormat}; rendererTrackFormats[rendererTrackCount] = new Format[] {trackFormat};
rendererExternalTrackFormats[rendererTrackCount++] = trackFormat; rendererExternalTrackFormats[rendererTrackCount++] = trackFormat;
} }
} }
} }
groupIndices[rendererIndex] = Arrays.copyOf(rendererTrackGroups, rendererTrackCount); trackSelections[rendererIndex] = Arrays.copyOf(rendererTrackSelections, rendererTrackCount);
trackIndices[rendererIndex] = Arrays.copyOf(rendererTrackIndices, rendererTrackCount);
trackFormats[rendererIndex] = Arrays.copyOf(rendererTrackFormats, rendererTrackCount); trackFormats[rendererIndex] = Arrays.copyOf(rendererTrackFormats, rendererTrackCount);
externalTrackFormats[rendererIndex] = Arrays.copyOf(rendererExternalTrackFormats, externalTrackFormats[rendererIndex] = Arrays.copyOf(rendererExternalTrackFormats,
rendererTrackCount); rendererTrackCount);
} }
boolean allRenderersEnded = true;
boolean allRenderersReadyOrEnded = true;
// Enable renderers where appropriate. // Enable renderers where appropriate.
for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) { for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
TrackRenderer renderer = renderers[rendererIndex]; TrackRenderer renderer = renderers[rendererIndex];
int trackIndex = selectedTrackIndices[rendererIndex]; int trackIndex = selectedTrackIndices[rendererIndex];
if (0 <= trackIndex && trackIndex < trackIndices[rendererIndex].length) { if (0 <= trackIndex && trackIndex < trackSelections[rendererIndex].length) {
TrackStream trackStream = source.enable(groupIndices[rendererIndex][trackIndex], TrackStream trackStream = source.enable(trackSelections[rendererIndex][trackIndex],
trackIndices[rendererIndex][trackIndex], positionUs); positionUs);
renderer.enable(trackFormats[rendererIndex][trackIndex], trackStream, positionUs, false); renderer.enable(trackFormats[rendererIndex][trackIndex], trackStream, positionUs, false);
enabledRenderers.add(renderer); enabledRenderers.add(renderer);
allRenderersEnded = allRenderersEnded && renderer.isEnded(); allRenderersEnded = allRenderersEnded && renderer.isEnded();
@ -572,7 +567,7 @@ import java.util.concurrent.atomic.AtomicInteger;
resetRendererInternal(renderers[i]); resetRendererInternal(renderers[i]);
} }
enabledRenderers.clear(); enabledRenderers.clear();
Arrays.fill(trackIndices, null); Arrays.fill(trackSelections, null);
source = null; source = null;
} }
@ -622,13 +617,13 @@ import java.util.concurrent.atomic.AtomicInteger;
TrackRenderer renderer = renderers[rendererIndex]; TrackRenderer renderer = renderers[rendererIndex];
int rendererState = renderer.getState(); int rendererState = renderer.getState();
if (trackIndices[rendererIndex].length == 0) { if (trackSelections[rendererIndex].length == 0) {
return; return;
} }
boolean isEnabled = rendererState == TrackRenderer.STATE_ENABLED boolean isEnabled = rendererState == TrackRenderer.STATE_ENABLED
|| rendererState == TrackRenderer.STATE_STARTED; || rendererState == TrackRenderer.STATE_STARTED;
boolean shouldEnable = 0 <= trackIndex && trackIndex < trackIndices[rendererIndex].length; boolean shouldEnable = 0 <= trackIndex && trackIndex < trackSelections[rendererIndex].length;
if (isEnabled) { if (isEnabled) {
// The renderer is currently enabled. We need to disable it, so that we can either re-enable // The renderer is currently enabled. We need to disable it, so that we can either re-enable
@ -650,8 +645,8 @@ import java.util.concurrent.atomic.AtomicInteger;
boolean playing = playWhenReady && state == ExoPlayer.STATE_READY; boolean playing = playWhenReady && state == ExoPlayer.STATE_READY;
// Consider as joining if the renderer was previously disabled, but not when switching tracks. // Consider as joining if the renderer was previously disabled, but not when switching tracks.
boolean joining = !isEnabled && playing; boolean joining = !isEnabled && playing;
TrackStream trackStream = source.enable(groupIndices[rendererIndex][trackIndex], TrackStream trackStream = source.enable(trackSelections[rendererIndex][trackIndex],
trackIndices[rendererIndex][trackIndex], positionUs); positionUs);
renderer.enable(trackFormats[rendererIndex][trackIndex], trackStream, positionUs, joining); renderer.enable(trackFormats[rendererIndex][trackIndex], trackStream, positionUs, joining);
enabledRenderers.add(renderer); enabledRenderers.add(renderer);
if (playing) { if (playing) {

View File

@ -156,13 +156,8 @@ public final class FrameworkSampleSource implements SampleSource {
} }
@Override @Override
public int getTrackGroupCount() { public TrackGroup[] getTrackGroups() {
return tracks.length; return tracks;
}
@Override
public TrackGroup getTrackGroup(int group) {
return tracks[group];
} }
@Override @Override
@ -171,14 +166,17 @@ public final class FrameworkSampleSource implements SampleSource {
} }
@Override @Override
public TrackStream enable(int group, int[] track, long positionUs) { public TrackStream enable(TrackSelection selection, long positionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
Assertions.checkState(trackStates[group] == TRACK_STATE_DISABLED); Assertions.checkState(selection.tracks.length == 1);
Assertions.checkState(selection.tracks[0] == 0);
int track = selection.group;
Assertions.checkState(trackStates[track] == TRACK_STATE_DISABLED);
enabledTrackCount++; enabledTrackCount++;
trackStates[group] = TRACK_STATE_ENABLED; trackStates[track] = TRACK_STATE_ENABLED;
extractor.selectTrack(group); extractor.selectTrack(track);
seekToUsInternal(positionUs, positionUs != 0); seekToUsInternal(positionUs, positionUs != 0);
return new TrackStreamImpl(group); return new TrackStreamImpl(track);
} }
/* package */ long readReset(int track) { /* package */ long readReset(int track) {

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.TraceUtil; import com.google.android.exoplayer.util.TraceUtil;

View File

@ -48,7 +48,7 @@ public class MultiSampleSource implements SampleSource {
this.durationUs = C.UNKNOWN_TIME_US; this.durationUs = C.UNKNOWN_TIME_US;
int totalTrackGroupCount = 0; int totalTrackGroupCount = 0;
for (int i = 0; i < sources.length; i++) { for (int i = 0; i < sources.length; i++) {
totalTrackGroupCount += sources[i].getTrackGroupCount(); totalTrackGroupCount += sources[i].getTrackGroups().length;
if (sources[i].getDurationUs() > durationUs) { if (sources[i].getDurationUs() > durationUs) {
durationUs = sources[i].getDurationUs(); durationUs = sources[i].getDurationUs();
} }
@ -56,9 +56,9 @@ public class MultiSampleSource implements SampleSource {
tracks = new TrackGroup[totalTrackGroupCount]; tracks = 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].getTrackGroupCount(); int sourceTrackGroupCount = sources[i].getTrackGroups().length;
for (int j = 0; j < sourceTrackGroupCount; j++) { for (int j = 0; j < sourceTrackGroupCount; j++) {
tracks[trackGroupIndex++] = sources[i].getTrackGroup(j); tracks[trackGroupIndex++] = sources[i].getTrackGroups()[j];
} }
} }
} }
@ -71,19 +71,15 @@ public class MultiSampleSource implements SampleSource {
} }
@Override @Override
public int getTrackGroupCount() { public TrackGroup[] getTrackGroups() {
return tracks.length; return tracks;
} }
@Override @Override
public TrackGroup getTrackGroup(int group) { public TrackStream enable(TrackSelection selection, long positionUs) {
return tracks[group]; Pair<Integer, Integer> sourceAndGroup = getSourceAndTrackGroupIndices(selection.group);
} return sources[sourceAndGroup.first].enable(
new TrackSelection(sourceAndGroup.second, selection.tracks), positionUs);
@Override
public TrackStream enable(int group, int[] tracks, long positionUs) {
Pair<Integer, Integer> sourceAndGroup = getSourceAndTrackGroupIndices(group);
return sources[sourceAndGroup.first].enable(sourceAndGroup.second, tracks, positionUs);
} }
@Override @Override
@ -132,7 +128,7 @@ public class MultiSampleSource implements SampleSource {
private Pair<Integer, Integer> getSourceAndTrackGroupIndices(int group) { private Pair<Integer, Integer> getSourceAndTrackGroupIndices(int group) {
int totalTrackGroupCount = 0; int totalTrackGroupCount = 0;
for (int i = 0; i < sources.length; i++) { for (int i = 0; i < sources.length; i++) {
int sourceTrackGroupCount = sources[i].getTrackGroupCount(); int sourceTrackGroupCount = sources[i].getTrackGroups().length;
if (group < totalTrackGroupCount + sourceTrackGroupCount) { if (group < totalTrackGroupCount + sourceTrackGroupCount) {
return Pair.create(i, group - totalTrackGroupCount); return Pair.create(i, group - totalTrackGroupCount);
} }

View File

@ -52,23 +52,13 @@ public interface SampleSource {
long getDurationUs(); long getDurationUs();
/** /**
* Returns the number of track groups exposed by the source. * Returns the {@link TrackGroup}s exposed by the source.
* <p> * <p>
* This method should only be called after the source has been prepared. * This method should only be called after the source has been prepared.
* *
* @return The number of track groups exposed by the source. * @return The {@link TrackGroup}s.
*/ */
public int getTrackGroupCount(); public TrackGroup[] getTrackGroups();
/**
* Returns the {@link TrackGroup} at the specified index.
* <p>
* This method should only be called after the source has been prepared.
*
* @int group The group index.
* @return The corresponding {@link TrackGroup}.
*/
public TrackGroup getTrackGroup(int group);
/** /**
* 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.
@ -101,19 +91,19 @@ public interface SampleSource {
void seekToUs(long positionUs); void seekToUs(long positionUs);
/** /**
* Enables the specified group to read the specified tracks. A {@link TrackStream} is returned * Enables the source to read a track defined by a {@link TrackSelection}. A {@link TrackStream}
* through which the enabled track's data can be read. * is returned through which the track's data can be read.
* <p> * <p>
* This method should only be called after the source has been prepared, and when the specified * This method should only be called after the source has been prepared, and when there are no
* group is disabled. Note that {@code tracks.length} is only permitted to be greater than one * other enabled tracks with the same {@link TrackSelection#group} index. Note that
* if {@link TrackGroup#adaptive} is true for the group. * {@code TrackSelection#tracks} must be of length 1 unless {@link TrackGroup#adaptive} is true
* for the group.
* *
* @param group The group index. * @param selection Defines the track.
* @param tracks The track indices.
* @param positionUs The current playback position in microseconds. * @param positionUs The current playback position in microseconds.
* @return A {@link TrackStream} from which the enabled track's data can be read. * @return A {@link TrackStream} from which the enabled track's data can be read.
*/ */
public TrackStream enable(int group, int[] tracks, long positionUs); public TrackStream enable(TrackSelection selection, long positionUs);
/** /**
* Releases the source. * Releases the source.
@ -122,77 +112,4 @@ public interface SampleSource {
*/ */
void release(); void release();
/**
* A stream of data corresponding to a single {@link SampleSource} track.
*/
interface TrackStream {
/**
* The end of stream has been reached.
*/
static final int END_OF_STREAM = -1;
/**
* Nothing was read.
*/
static final int NOTHING_READ = -2;
/**
* A sample was read.
*/
static final int SAMPLE_READ = -3;
/**
* A format was read.
*/
static final int FORMAT_READ = -4;
/**
* Returned from {@link #readReset()} to indicate no reset is required.
*/
static final long NO_RESET = Long.MIN_VALUE;
/**
* Returns whether data is available to be read.
* <p>
* Note: If the stream has ended then {@link #END_OF_STREAM} can always be read from
* {@link #readData(FormatHolder, SampleHolder)}. Hence an ended stream is always ready.
*
* @return True if data is available to be read. False otherwise.
*/
boolean isReady();
/**
* If there's an underlying error preventing data from being read, it's thrown by this method.
* If not, this method does nothing.
*
* @throws IOException The underlying error.
*/
void maybeThrowError() throws IOException;
/**
* Attempts to read a pending reset.
*
* @return If a reset was read then the position after the reset. Else {@link #NO_RESET}.
*/
long readReset();
/**
* Attempts to read the next format or sample.
* <p>
* This method will always return {@link #NOTHING_READ} in the case that there's a pending
* discontinuity to be read from {@link #readReset} for the specified track.
*
* @param formatHolder A {@link FormatHolder} to populate in the case of a new format.
* @param sampleHolder A {@link SampleHolder} to populate in the case of a new sample. If the
* caller requires the sample data then it must ensure that {@link SampleHolder#data}
* references a valid output buffer.
* @return The result, which can be {@link #END_OF_STREAM}, {@link #NOTHING_READ},
* {@link #FORMAT_READ} or {@link #SAMPLE_READ}.
*/
int readData(FormatHolder formatHolder, SampleHolder sampleHolder);
/**
* Disables the track.
*/
void disable();
}
} }

View File

@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import java.io.IOException; import java.io.IOException;

View File

@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
@ -53,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 tracks; private final TrackGroup[] trackGroups;
private int state; private int state;
private byte[] sampleData; private byte[] sampleData;
@ -76,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;
tracks = new TrackGroup(format); trackGroups = new TrackGroup[] {new TrackGroup(format)};
sampleData = new byte[INITIAL_SAMPLE_SIZE]; sampleData = new byte[INITIAL_SAMPLE_SIZE];
} }
@ -106,17 +105,12 @@ public final class SingleSampleSource implements SampleSource, TrackStream, Load
} }
@Override @Override
public int getTrackGroupCount() { public TrackGroup[] getTrackGroups() {
return 1; return trackGroups;
} }
@Override @Override
public TrackGroup getTrackGroup(int group) { public TrackStream enable(TrackSelection selection, long positionUs) {
return tracks;
}
@Override
public TrackStream enable(int group, int[] tracks, long positionUs) {
state = STATE_SEND_FORMAT; state = STATE_SEND_FORMAT;
clearCurrentLoadableException(); clearCurrentLoadableException();
maybeStartLoading(); maybeStartLoading();

View File

@ -15,8 +15,6 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.SampleSource.TrackStream;
/** /**
* Defines a group of tracks exposed by a {@link SampleSource}. * Defines a group of tracks exposed by a {@link SampleSource}.
* <p> * <p>

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent; import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2014 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;
/**
* Defines a track selection.
*/
public final class TrackSelection {
/**
* The index of the {@link TrackGroup}.
*/
public final int group;
/**
* The indices of the individual tracks within the {@link TrackGroup}.
*/
public final int[] tracks;
/**
* @param group The index of the {@link TrackGroup}.
* @param tracks The indices of the individual tracks within the {@link TrackGroup}.
*/
public TrackSelection(int group, int... tracks) {
this.group = group;
this.tracks = tracks;
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2014 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.io.IOException;
/**
* A stream of media data.
*/
public interface TrackStream {
/**
* The end of stream has been reached.
*/
int END_OF_STREAM = -1;
/**
* Nothing was read.
*/
int NOTHING_READ = -2;
/**
* A sample was read.
*/
int SAMPLE_READ = -3;
/**
* A format was read.
*/
int FORMAT_READ = -4;
/**
* Returned from {@link #readReset()} to indicate no reset is required.
*/
long NO_RESET = Long.MIN_VALUE;
/**
* Returns whether data is available to be read.
* <p>
* Note: If the stream has ended then {@link #END_OF_STREAM} can always be read from
* {@link #readData(FormatHolder, SampleHolder)}. Hence an ended stream is always ready.
*
* @return True if data is available to be read. False otherwise.
*/
boolean isReady();
/**
* If there's an underlying error preventing data from being read, it's thrown by this method.
* If not, this method does nothing.
*
* @throws IOException The underlying error.
*/
void maybeThrowError() throws IOException;
/**
* Attempts to read a pending reset.
*
* @return If a reset was read then the position after the reset. Else {@link #NO_RESET}.
*/
long readReset();
/**
* Attempts to read the next format or sample.
* <p>
* This method will always return {@link #NOTHING_READ} in the case that there's a pending
* discontinuity to be read from {@link #readReset} for the specified track.
*
* @param formatHolder A {@link FormatHolder} to populate in the case of a new format.
* @param sampleHolder A {@link SampleHolder} to populate in the case of a new sample. If the
* caller requires the sample data then it must ensure that {@link SampleHolder#data}
* references a valid output buffer.
* @return The result, which can be {@link #END_OF_STREAM}, {@link #NOTHING_READ},
* {@link #FORMAT_READ} or {@link #SAMPLE_READ}.
*/
int readData(FormatHolder formatHolder, SampleHolder sampleHolder);
/**
* Disables the track.
*/
void disable();
}

View File

@ -18,7 +18,7 @@ package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.TrackStream; import com.google.android.exoplayer.TrackStream;
import java.io.IOException; import java.io.IOException;

View File

@ -21,8 +21,9 @@ import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.LoadControl; 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.SampleSource.TrackStream;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Loadable; import com.google.android.exoplayer.upstream.Loader.Loadable;
@ -77,6 +78,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 long durationUs; private long durationUs;
private Loader loader; private Loader loader;
private boolean loadingFinished; private boolean loadingFinished;
@ -156,6 +158,9 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
TrackGroup trackGroup = chunkSource.getTracks(); TrackGroup trackGroup = chunkSource.getTracks();
if (trackGroup.length > 0) { if (trackGroup.length > 0) {
loader = new Loader("Loader:" + trackGroup.getFormat(0).containerMimeType); loader = new Loader("Loader:" + trackGroup.getFormat(0).containerMimeType);
trackGroups = new TrackGroup[] {trackGroup};
} else {
trackGroups = new TrackGroup[0];
} }
state = STATE_PREPARED; state = STATE_PREPARED;
return true; return true;
@ -172,22 +177,17 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
} }
@Override @Override
public int getTrackGroupCount() { public TrackGroup[] getTrackGroups() {
return 1;
}
@Override
public TrackGroup getTrackGroup(int group) {
Assertions.checkState(state != STATE_IDLE); Assertions.checkState(state != STATE_IDLE);
return chunkSource.getTracks(); return trackGroups;
} }
@Override @Override
public TrackStream enable(int group, int[] tracks, long positionUs) { public TrackStream enable(TrackSelection selection, long positionUs) {
Assertions.checkState(state == STATE_PREPARED); Assertions.checkState(state == STATE_PREPARED);
Assertions.checkState(enabledTrackCount++ == 0); Assertions.checkState(enabledTrackCount++ == 0);
state = STATE_ENABLED; state = STATE_ENABLED;
chunkSource.enable(tracks); chunkSource.enable(selection.tracks);
loadControl.register(this, bufferSizeContribution); loadControl.register(this, bufferSizeContribution);
downstreamFormat = null; downstreamFormat = null;
downstreamSampleFormat = null; downstreamSampleFormat = null;

View File

@ -21,6 +21,8 @@ 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.TrackSelection;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.upstream.Allocator; import com.google.android.exoplayer.upstream.Allocator;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
@ -288,23 +290,21 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
} }
@Override @Override
public int getTrackGroupCount() { public TrackGroup[] getTrackGroups() {
return tracks.length; return tracks;
} }
@Override @Override
public TrackGroup getTrackGroup(int group) { public TrackStream enable(TrackSelection selection, long positionUs) {
return tracks[group];
}
@Override
public TrackStream enable(int group, int[] tracks, long positionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
Assertions.checkState(!trackEnabledStates[group]); Assertions.checkState(selection.tracks.length == 1);
Assertions.checkState(selection.tracks[0] == 0);
int track = selection.group;
Assertions.checkState(!trackEnabledStates[track]);
enabledTrackCount++; enabledTrackCount++;
trackEnabledStates[group] = true; trackEnabledStates[track] = true;
pendingMediaFormat[group] = true; pendingMediaFormat[track] = true;
pendingResets[group] = false; pendingResets[track] = false;
if (enabledTrackCount == 1) { if (enabledTrackCount == 1) {
// Treat all enables in non-seekable media as being from t=0. // Treat all enables in non-seekable media as being from t=0.
positionUs = !seekMap.isSeekable() ? 0 : positionUs; positionUs = !seekMap.isSeekable() ? 0 : positionUs;
@ -312,7 +312,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
restartFrom(positionUs); restartFrom(positionUs);
} }
return new TrackStreamImpl(group); return new TrackStreamImpl(track);
} }
/* package */ void disable(int track) { /* package */ void disable(int track) {

View File

@ -22,6 +22,8 @@ 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.TrackSelection;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener; import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
import com.google.android.exoplayer.chunk.Chunk; import com.google.android.exoplayer.chunk.Chunk;
import com.google.android.exoplayer.chunk.ChunkOperationHolder; import com.google.android.exoplayer.chunk.ChunkOperationHolder;
@ -179,19 +181,16 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
} }
@Override @Override
public int getTrackGroupCount() { public TrackGroup[] getTrackGroups() {
return trackGroups.length; Assertions.checkState(prepared);
return trackGroups;
} }
@Override @Override
public TrackGroup getTrackGroup(int group) { public TrackStream enable(TrackSelection selection, long positionUs) {
Assertions.checkState(prepared);
return trackGroups[group];
}
@Override
public TrackStream enable(int group, int[] tracks, long positionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
int group = selection.group;
int[] tracks = selection.tracks;
setTrackGroupEnabledState(group, true); setTrackGroupEnabledState(group, true);
downstreamSampleFormats[group] = null; downstreamSampleFormats[group] = null;
pendingResets[group] = false; pendingResets[group] = false;

View File

@ -19,9 +19,9 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.SampleSourceTrackRenderer; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import android.os.Handler; import android.os.Handler;

View File

@ -19,9 +19,9 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.SampleSourceTrackRenderer; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import android.annotation.TargetApi; import android.annotation.TargetApi;

View File

@ -20,9 +20,9 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource.TrackStream;
import com.google.android.exoplayer.SampleSourceTrackRenderer; import com.google.android.exoplayer.SampleSourceTrackRenderer;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.Cue;
import com.google.android.exoplayer.text.TextRenderer; import com.google.android.exoplayer.text.TextRenderer;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;