Don't reuse SampleStream instance in SingleSampleMediaSource

Referential equality is going to become important for detecting
whether a SampleStream has been replaced, so we need to create
new instances as we do elsewhere.

This also enables multiple SampleStreams to be provided for a
single TrackGroup, as is also true for DASH and SmoothStreaming.
It's forbidden to ask for multiple SampleStreams from a single
TrackGroup currently, but we may choose to relax that at some
point (and indicate whether it's allowed as a flag on each
TrackGroup).

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=129842336
This commit is contained in:
olly 2016-08-10 01:45:08 -07:00 committed by Oliver Woodman
parent 665c9fc14c
commit aab4e36d1a

View File

@ -29,13 +29,14 @@ import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.upstream.Loader.Loadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
* Loads data at a given {@link Uri} as a single sample belonging to a single {@link MediaPeriod}. * Loads data at a given {@link Uri} as a single sample belonging to a single {@link MediaPeriod}.
*/ */
public final class SingleSampleMediaSource implements MediaPeriod, MediaSource, SampleStream, public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
Loader.Callback<SingleSampleMediaSource.SourceLoadable> { Loader.Callback<SingleSampleMediaSource.SourceLoadable> {
/** /**
@ -63,26 +64,21 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
*/ */
private static final int INITIAL_SAMPLE_SIZE = 1; private static final int INITIAL_SAMPLE_SIZE = 1;
private static final int STREAM_STATE_SEND_FORMAT = 0;
private static final int STREAM_STATE_SEND_SAMPLE = 1;
private static final int STREAM_STATE_END_OF_STREAM = 2;
private final Uri uri; private final Uri uri;
private final DataSource.Factory dataSourceFactory; private final DataSource.Factory dataSourceFactory;
private final Format format;
private final long durationUs; private final long durationUs;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final TrackGroupArray tracks; private final TrackGroupArray tracks;
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final EventListener eventListener;
private final int eventSourceId; private final int eventSourceId;
private final ArrayList<SampleStreamImpl> sampleStreams;
/* package */ final Format format;
private Loader loader; /* package */ Loader loader;
private boolean loadingFinished; /* package */ boolean loadingFinished;
/* package */ byte[] sampleData;
private int streamState; /* package */ int sampleSize;
private byte[] sampleData;
private int sampleSize;
public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format, public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs) { long durationUs) {
@ -107,7 +103,7 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
this.eventSourceId = eventSourceId; this.eventSourceId = eventSourceId;
tracks = new TrackGroupArray(new TrackGroup(format)); tracks = new TrackGroupArray(new TrackGroup(format));
sampleData = new byte[INITIAL_SAMPLE_SIZE]; sampleData = new byte[INITIAL_SAMPLE_SIZE];
streamState = STREAM_STATE_SEND_FORMAT; sampleStreams = new ArrayList<>();
} }
// MediaSource implementation. // MediaSource implementation.
@ -165,13 +161,15 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
@Override @Override
public SampleStream[] selectTracks(List<SampleStream> oldStreams, public SampleStream[] selectTracks(List<SampleStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) { List<TrackSelection> newSelections, long positionUs) {
Assertions.checkState(oldStreams.size() <= 1); for (int i = 0; i < oldStreams.size(); i++) {
Assertions.checkState(newSelections.size() <= 1); SampleStreamImpl oldStream = (SampleStreamImpl) oldStreams.get(i);
// Select new tracks. sampleStreams.remove(oldStream);
}
SampleStream[] newStreams = new SampleStream[newSelections.size()]; SampleStream[] newStreams = new SampleStream[newSelections.size()];
if (!newSelections.isEmpty()) { for (int i = 0; i < newStreams.length; i++) {
newStreams[0] = this; SampleStreamImpl newStream = new SampleStreamImpl();
streamState = STREAM_STATE_SEND_FORMAT; sampleStreams.add(newStream);
newStreams[i] = newStream;
} }
return newStreams; return newStreams;
} }
@ -186,6 +184,11 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
return true; return true;
} }
@Override
public long readDiscontinuity() {
return C.UNSET_TIME_US;
}
@Override @Override
public long getNextLoadPositionUs() { public long getNextLoadPositionUs() {
return loadingFinished || loader.isLoading() ? C.END_OF_SOURCE_US : 0; return loadingFinished || loader.isLoading() ? C.END_OF_SOURCE_US : 0;
@ -198,8 +201,8 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
@Override @Override
public long seekToUs(long positionUs) { public long seekToUs(long positionUs) {
if (streamState == STREAM_STATE_END_OF_STREAM) { for (int i = 0; i < sampleStreams.size(); i++) {
streamState = STREAM_STATE_SEND_SAMPLE; sampleStreams.get(i).seekToUs(positionUs);
} }
return positionUs; return positionUs;
} }
@ -211,57 +214,11 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
loader = null; loader = null;
} }
loadingFinished = false; loadingFinished = false;
streamState = STREAM_STATE_SEND_FORMAT; sampleStreams.clear();
sampleData = null; sampleData = null;
sampleSize = 0; sampleSize = 0;
} }
// SampleStream implementation.
@Override
public boolean isReady() {
return loadingFinished;
}
@Override
public void maybeThrowError() throws IOException {
loader.maybeThrowError();
}
@Override
public long readDiscontinuity() {
return C.UNSET_TIME_US;
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
if (streamState == STREAM_STATE_END_OF_STREAM) {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
} else if (streamState == STREAM_STATE_SEND_FORMAT) {
formatHolder.format = format;
streamState = STREAM_STATE_SEND_SAMPLE;
return C.RESULT_FORMAT_READ;
}
Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE);
if (!loadingFinished) {
return C.RESULT_NOTHING_READ;
} else {
buffer.timeUs = 0;
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.ensureSpaceForWrite(sampleSize);
buffer.data.put(sampleData, 0, sampleSize);
streamState = STREAM_STATE_END_OF_STREAM;
return C.RESULT_BUFFER_READ;
}
}
@Override
public void skipToKeyframeBefore(long timeUs) {
// do nothing
}
// Loader.Callback implementation. // Loader.Callback implementation.
@Override @Override
@ -298,6 +255,61 @@ public final class SingleSampleMediaSource implements MediaPeriod, MediaSource,
} }
} }
private final class SampleStreamImpl implements SampleStream {
private static final int STREAM_STATE_SEND_FORMAT = 0;
private static final int STREAM_STATE_SEND_SAMPLE = 1;
private static final int STREAM_STATE_END_OF_STREAM = 2;
private int streamState;
public void seekToUs(long positionUs) {
if (streamState == STREAM_STATE_END_OF_STREAM) {
streamState = STREAM_STATE_SEND_SAMPLE;
}
}
@Override
public boolean isReady() {
return loadingFinished;
}
@Override
public void maybeThrowError() throws IOException {
loader.maybeThrowError();
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
if (streamState == STREAM_STATE_END_OF_STREAM) {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
} else if (streamState == STREAM_STATE_SEND_FORMAT) {
formatHolder.format = format;
streamState = STREAM_STATE_SEND_SAMPLE;
return C.RESULT_FORMAT_READ;
}
Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE);
if (!loadingFinished) {
return C.RESULT_NOTHING_READ;
} else {
buffer.timeUs = 0;
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.ensureSpaceForWrite(sampleSize);
buffer.data.put(sampleData, 0, sampleSize);
streamState = STREAM_STATE_END_OF_STREAM;
return C.RESULT_BUFFER_READ;
}
}
@Override
public void skipToKeyframeBefore(long timeUs) {
// do nothing
}
}
/* package */ static final class SourceLoadable implements Loadable { /* package */ static final class SourceLoadable implements Loadable {
private final Uri uri; private final Uri uri;