Optimize seeking for unseekable SeekMaps

- Avoid re-downloading data prior to the first mdat box when
  seeking back to the start of an unseekable FMP4.
- Avoid re-downloading data prior to the first frame for
  constant bitrate MP3.
- Update SeekMap.getPosition documentation to allow a non-zero
  position for the unseekable case. Note that XingSeeker was
  already returning a non-zero position if unseekable.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=177317256
This commit is contained in:
olly 2017-11-29 09:09:17 -08:00 committed by Oliver Woodman
parent 21ea9a821d
commit a99ef01d3a
3 changed files with 17 additions and 4 deletions

View File

@ -28,13 +28,24 @@ public interface SeekMap {
final class Unseekable implements SeekMap { final class Unseekable implements SeekMap {
private final long durationUs; private final long durationUs;
private final long startPosition;
/** /**
* @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if * @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if
* the duration is unknown. * the duration is unknown.
*/ */
public Unseekable(long durationUs) { public Unseekable(long durationUs) {
this(durationUs, 0);
}
/**
* @param durationUs The duration of the stream in microseconds, or {@link C#TIME_UNSET} if
* the duration is unknown.
* @param startPosition The position (byte offset) of the start of the media.
*/
public Unseekable(long durationUs, long startPosition) {
this.durationUs = durationUs; this.durationUs = durationUs;
this.startPosition = startPosition;
} }
@Override @Override
@ -49,7 +60,7 @@ public interface SeekMap {
@Override @Override
public long getPosition(long timeUs) { public long getPosition(long timeUs) {
return 0; return startPosition;
} }
} }
@ -78,7 +89,8 @@ public interface SeekMap {
* *
* @param timeUs A seek position in microseconds. * @param timeUs A seek position in microseconds.
* @return The corresponding position (byte offset) in the stream from which data can be provided * @return The corresponding position (byte offset) in the stream from which data can be provided
* to the extractor, or 0 if {@code #isSeekable()} returns false. * to the extractor. If {@link #isSeekable()} returns false then the returned value will be
* independent of {@code timeUs}, and will indicate the start of the media in the stream.
*/ */
long getPosition(long timeUs); long getPosition(long timeUs);

View File

@ -43,7 +43,7 @@ import com.google.android.exoplayer2.util.Util;
@Override @Override
public long getPosition(long timeUs) { public long getPosition(long timeUs) {
if (durationUs == C.TIME_UNSET) { if (durationUs == C.TIME_UNSET) {
return 0; return firstFramePosition;
} }
timeUs = Util.constrainValue(timeUs, 0, durationUs); timeUs = Util.constrainValue(timeUs, 0, durationUs);
return firstFramePosition + (timeUs * bitrate) / (C.MICROS_PER_SECOND * BITS_PER_BYTE); return firstFramePosition + (timeUs * bitrate) / (C.MICROS_PER_SECOND * BITS_PER_BYTE);

View File

@ -345,7 +345,8 @@ public final class FragmentedMp4Extractor implements Extractor {
currentTrackBundle = null; currentTrackBundle = null;
endOfMdatPosition = atomPosition + atomSize; endOfMdatPosition = atomPosition + atomSize;
if (!haveOutputSeekMap) { if (!haveOutputSeekMap) {
extractorOutput.seekMap(new SeekMap.Unseekable(durationUs)); // This must be the first mdat in the stream.
extractorOutput.seekMap(new SeekMap.Unseekable(durationUs, atomPosition));
haveOutputSeekMap = true; haveOutputSeekMap = true;
} }
parserState = STATE_READING_ENCRYPTION_DATA; parserState = STATE_READING_ENCRYPTION_DATA;