Commit 64cc380f by Oliver Woodman

Avoid loading first chunk when preparing HLS for non-zero position.

This also fixes a technical mistake where HlsChunkSource is fed
seekPositionUs=-1 when obtaining the first chunk. This is wrong,
but the usage of this variable within HlsChunkSource enforces that
the seek must stay within bounds, so we get away with it.

Issue: #385
parent 116a1884
...@@ -57,7 +57,7 @@ import android.widget.TextView; ...@@ -57,7 +57,7 @@ import android.widget.TextView;
} }
@Override @Override
protected int doPrepare() throws ExoPlaybackException { protected int doPrepare(long positionUs) throws ExoPlaybackException {
maybeFail(); maybeFail();
return STATE_PREPARED; return STATE_PREPARED;
} }
......
...@@ -18,14 +18,14 @@ package com.google.android.exoplayer; ...@@ -18,14 +18,14 @@ package com.google.android.exoplayer;
/** /**
* A {@link TrackRenderer} that does nothing. * A {@link TrackRenderer} that does nothing.
* <p> * <p>
* This renderer returns {@link TrackRenderer#STATE_IGNORE} from {@link #doPrepare()} in order to * This renderer returns {@link TrackRenderer#STATE_IGNORE} from {@link #doPrepare(long)} in order
* request that it should be ignored. {@link IllegalStateException} is thrown from all methods that * to request that it should be ignored. {@link IllegalStateException} is thrown from all methods
* are documented to indicate that they should not be invoked unless the renderer is prepared. * that are documented to indicate that they should not be invoked unless the renderer is prepared.
*/ */
public class DummyTrackRenderer extends TrackRenderer { public class DummyTrackRenderer extends TrackRenderer {
@Override @Override
protected int doPrepare() throws ExoPlaybackException { protected int doPrepare(long positionUs) throws ExoPlaybackException {
return STATE_IGNORE; return STATE_IGNORE;
} }
......
...@@ -264,7 +264,7 @@ import java.util.List; ...@@ -264,7 +264,7 @@ import java.util.List;
boolean prepared = true; boolean prepared = true;
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
if (renderers[i].getState() == TrackRenderer.STATE_UNPREPARED) { if (renderers[i].getState() == TrackRenderer.STATE_UNPREPARED) {
int state = renderers[i].prepare(); int state = renderers[i].prepare(positionUs);
if (state == TrackRenderer.STATE_UNPREPARED) { if (state == TrackRenderer.STATE_UNPREPARED) {
prepared = false; prepared = false;
} }
......
...@@ -128,7 +128,7 @@ public final class FrameworkSampleSource implements SampleSource { ...@@ -128,7 +128,7 @@ public final class FrameworkSampleSource implements SampleSource {
} }
@Override @Override
public boolean prepare() throws IOException { public boolean prepare(long positionUs) throws IOException {
if (!prepared) { if (!prepared) {
extractor = new MediaExtractor(); extractor = new MediaExtractor();
if (context != null) { if (context != null) {
......
...@@ -243,9 +243,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -243,9 +243,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected int doPrepare() throws ExoPlaybackException { protected int doPrepare(long positionUs) throws ExoPlaybackException {
try { try {
boolean sourcePrepared = source.prepare(); boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) { if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED; return TrackRenderer.STATE_UNPREPARED;
} }
......
...@@ -57,10 +57,11 @@ public interface SampleSource { ...@@ -57,10 +57,11 @@ public interface SampleSource {
* and formats). If insufficient data is available then the call will return {@code false} rather * and formats). If insufficient data is available then the call will return {@code false} rather
* than block. The method can be called repeatedly until the return value indicates success. * than block. The method can be called repeatedly until the return value indicates success.
* *
* @param positionUs The player's current playback position.
* @return True if the source was prepared successfully, false otherwise. * @return True if the source was prepared successfully, false otherwise.
* @throws IOException If an error occurred preparing the source. * @throws IOException If an error occurred preparing the source.
*/ */
public boolean prepare() throws IOException; public boolean prepare(long positionUs) throws IOException;
/** /**
* Returns the number of tracks exposed by the source. * Returns the number of tracks exposed by the source.
......
...@@ -108,11 +108,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -108,11 +108,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* Prepares the renderer. This method is non-blocking, and hence it may be necessary to call it * Prepares the renderer. This method is non-blocking, and hence it may be necessary to call it
* more than once in order to transition the renderer into the prepared state. * more than once in order to transition the renderer into the prepared state.
* *
* @param positionUs The player's current playback position.
* @return The current state (one of the STATE_* constants), for convenience. * @return The current state (one of the STATE_* constants), for convenience.
*/ */
/* package */ final int prepare() throws ExoPlaybackException { /* package */ final int prepare(long positionUs) throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED); Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED);
state = doPrepare(); state = doPrepare(positionUs);
Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED || Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED ||
state == TrackRenderer.STATE_PREPARED || state == TrackRenderer.STATE_PREPARED ||
state == TrackRenderer.STATE_IGNORE); state == TrackRenderer.STATE_IGNORE);
...@@ -127,11 +128,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -127,11 +128,12 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* This method should return quickly, and should not block if the renderer is currently unable to * This method should return quickly, and should not block if the renderer is currently unable to
* make any useful progress. * make any useful progress.
* *
* @param positionUs The player's current playback position.
* @return The new state of the renderer. One of {@link #STATE_UNPREPARED}, * @return The new state of the renderer. One of {@link #STATE_UNPREPARED},
* {@link #STATE_PREPARED} and {@link #STATE_IGNORE}. * {@link #STATE_PREPARED} and {@link #STATE_IGNORE}.
* @throws ExoPlaybackException If an error occurs. * @throws ExoPlaybackException If an error occurs.
*/ */
protected abstract int doPrepare() throws ExoPlaybackException; protected abstract int doPrepare(long positionUs) throws ExoPlaybackException;
/** /**
* Enable the renderer. * Enable the renderer.
......
...@@ -121,7 +121,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback { ...@@ -121,7 +121,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Callback {
} }
@Override @Override
public boolean prepare() { public boolean prepare(long positionUs) {
Assertions.checkState(state == STATE_UNPREPARED); Assertions.checkState(state == STATE_UNPREPARED);
loader = new Loader("Loader:" + chunkSource.getTrackInfo().mimeType); loader = new Loader("Loader:" + chunkSource.getTrackInfo().mimeType);
state = STATE_PREPARED; state = STATE_PREPARED;
......
...@@ -138,7 +138,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa ...@@ -138,7 +138,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
} }
@Override @Override
public boolean prepare() throws IOException { public boolean prepare(long positionUs) throws IOException {
if (prepared) { if (prepared) {
return true; return true;
} }
......
...@@ -125,15 +125,12 @@ public class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -125,15 +125,12 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
} }
@Override @Override
public boolean prepare() throws IOException { public boolean prepare(long positionUs) throws IOException {
if (prepared) { if (prepared) {
return true; return true;
} }
if (loader == null) {
loader = new Loader("Loader:HLS");
}
continueBufferingInternal();
if (!extractors.isEmpty()) { if (!extractors.isEmpty()) {
// We're not prepared, but we might have loaded what we need.
HlsExtractorWrapper extractor = extractors.getFirst(); HlsExtractorWrapper extractor = extractors.getFirst();
if (extractor.isPrepared()) { if (extractor.isPrepared()) {
trackCount = extractor.getTrackCount(); trackCount = extractor.getTrackCount();
...@@ -146,12 +143,23 @@ public class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -146,12 +143,23 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
trackInfos[i] = new TrackInfo(format.mimeType, chunkSource.getDurationUs()); trackInfos[i] = new TrackInfo(format.mimeType, chunkSource.getDurationUs());
} }
prepared = true; prepared = true;
return true;
} }
} }
if (!prepared) { // We're not prepared and we haven't loaded what we need.
maybeThrowLoadableException(); if (loader == null) {
loader = new Loader("Loader:HLS");
} }
return prepared; if (!loader.isLoading()) {
// We're going to have to start loading a chunk to get what we need for preparation. We should
// attempt to load the chunk at positionUs, so that we'll already be loading the correct chunk
// in the common case where the renderer is subsequently enabled at this position.
pendingResetPositionUs = positionUs;
downstreamPositionUs = positionUs;
}
maybeStartLoading();
maybeThrowLoadableException();
return false;
} }
@Override @Override
......
...@@ -90,9 +90,9 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback ...@@ -90,9 +90,9 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback
} }
@Override @Override
protected int doPrepare() throws ExoPlaybackException { protected int doPrepare(long positionUs) throws ExoPlaybackException {
try { try {
boolean sourcePrepared = source.prepare(); boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) { if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED; return TrackRenderer.STATE_UNPREPARED;
} }
......
...@@ -80,9 +80,9 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { ...@@ -80,9 +80,9 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected int doPrepare() throws ExoPlaybackException { protected int doPrepare(long positionUs) throws ExoPlaybackException {
try { try {
boolean sourcePrepared = source.prepare(); boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) { if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED; return TrackRenderer.STATE_UNPREPARED;
} }
......
...@@ -90,9 +90,9 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { ...@@ -90,9 +90,9 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected int doPrepare() throws ExoPlaybackException { protected int doPrepare(long positionUs) throws ExoPlaybackException {
try { try {
boolean sourcePrepared = source.prepare(); boolean sourcePrepared = source.prepare(positionUs);
if (!sourcePrepared) { if (!sourcePrepared) {
return TrackRenderer.STATE_UNPREPARED; return TrackRenderer.STATE_UNPREPARED;
} }
......
...@@ -579,7 +579,7 @@ public class Mp4ExtractorTest extends TestCase { ...@@ -579,7 +579,7 @@ public class Mp4ExtractorTest extends TestCase {
try { try {
switch (message.what) { switch (message.what) {
case MSG_PREPARE: case MSG_PREPARE:
if (!source.prepare()) { if (!source.prepare(0)) {
sendEmptyMessage(MSG_PREPARE); sendEmptyMessage(MSG_PREPARE);
} else { } else {
// Select the video track and get its metadata. // Select the video track and get its metadata.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment