diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsExtractor.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsExtractor.java
index a22e17ce85..5915ea6885 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsExtractor.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsExtractor.java
@@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer.hls.parser;
-import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.util.ParsableByteArray;
@@ -43,23 +42,9 @@ public class AdtsExtractor implements HlsExtractor {
}
@Override
- public void init(ExtractorOutput output) {
- adtsReader = new AdtsReader(output.getTrackOutput(0));
- }
-
- @Override
- public int getTrackCount() {
- return 1;
- }
-
- @Override
- public MediaFormat getFormat(int track) {
- return adtsReader.getFormat();
- }
-
- @Override
- public boolean isPrepared() {
- return adtsReader != null && adtsReader.hasFormat();
+ public void init(TrackOutputBuilder output) {
+ adtsReader = new AdtsReader(output.buildOutput(0));
+ output.allOutputsBuilt();
}
@Override
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsReader.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsReader.java
index 81c643c7a8..fc7417a60a 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsReader.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/AdtsReader.java
@@ -152,7 +152,7 @@ import java.util.Collections;
private void parseHeader() {
adtsScratch.setPosition(0);
- if (!hasFormat()) {
+ if (!output.hasFormat()) {
int audioObjectType = adtsScratch.readBits(2) + 1;
int sampleRateIndex = adtsScratch.readBits(4);
adtsScratch.skipBits(1);
@@ -167,7 +167,7 @@ import java.util.Collections;
MediaFormat.NO_VALUE, audioParams.second, audioParams.first,
Collections.singletonList(audioSpecificConfig));
frameDurationUs = (C.MICROS_PER_SECOND * 1024L) / mediaFormat.sampleRate;
- setFormat(mediaFormat);
+ output.setFormat(mediaFormat);
} else {
adtsScratch.skipBits(10);
}
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/ElementaryStreamReader.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/ElementaryStreamReader.java
index 8522cbeb18..a441ff35b9 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/ElementaryStreamReader.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/ElementaryStreamReader.java
@@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer.hls.parser;
-import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.hls.parser.HlsExtractor.TrackOutput;
import com.google.android.exoplayer.util.ParsableByteArray;
@@ -25,7 +24,6 @@ import com.google.android.exoplayer.util.ParsableByteArray;
/* package */ abstract class ElementaryStreamReader {
protected final TrackOutput output;
- private MediaFormat format;
/**
* @param output A {@link TrackOutput} to which samples should be written.
@@ -34,29 +32,6 @@ import com.google.android.exoplayer.util.ParsableByteArray;
this.output = output;
}
- /**
- * True if the format of the stream is known. False otherwise.
- */
- public boolean hasFormat() {
- return format != null;
- }
-
- /**
- * Returns the format of the stream, or {@code null} if {@link #hasFormat()} is false.
- */
- public MediaFormat getFormat() {
- return format;
- }
-
- /**
- * Sets the format of the stream.
- *
- * @param format The format.
- */
- protected void setFormat(MediaFormat format) {
- this.format = format;
- }
-
/**
* Consumes (possibly partial) payload data.
*
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/H264Reader.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/H264Reader.java
index ae9d0fe7a9..40b3b1ca32 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/H264Reader.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/H264Reader.java
@@ -88,7 +88,7 @@ import java.util.List;
int nalUnitOffsetInData = nextNalUnitOffset - limit;
if (nalUnitType == NAL_UNIT_TYPE_AUD) {
if (output.isWritingSample()) {
- if (isKeyframe && !hasFormat() && sps.isCompleted() && pps.isCompleted()) {
+ if (isKeyframe && !output.hasFormat() && sps.isCompleted() && pps.isCompleted()) {
parseMediaFormat(sps, pps);
}
output.commitSample(isKeyframe ? C.SAMPLE_FLAG_SYNC : 0, nalUnitOffsetInData, null);
@@ -120,7 +120,7 @@ import java.util.List;
}
private void feedNalUnitTargetBuffersStart(int nalUnitType) {
- if (!hasFormat()) {
+ if (!output.hasFormat()) {
sps.startNalUnit(nalUnitType);
pps.startNalUnit(nalUnitType);
}
@@ -128,7 +128,7 @@ import java.util.List;
}
private void feedNalUnitTargetBuffersData(byte[] dataArray, int offset, int limit) {
- if (!hasFormat()) {
+ if (!output.hasFormat()) {
sps.appendToNalUnit(dataArray, offset, limit);
pps.appendToNalUnit(dataArray, offset, limit);
}
@@ -233,7 +233,7 @@ import java.util.List;
}
// Set the format.
- setFormat(MediaFormat.createVideoFormat(MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE,
+ output.setFormat(MediaFormat.createVideoFormat(MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE,
frameWidth, frameHeight, initializationData));
}
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractor.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractor.java
index 92a4ac6717..e3b3e5468f 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractor.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractor.java
@@ -29,15 +29,21 @@ public interface HlsExtractor {
/**
* An object to which extracted data should be output.
*/
- public interface ExtractorOutput {
+ public interface TrackOutputBuilder {
/**
- * Obtains a {@link TrackOutput} to which extracted data should be output for a given track.
+ * Invoked to build a {@link TrackOutput} to which data should be output for a given track.
*
* @param trackId A stable track id.
* @return The corresponding {@link TrackOutput}.
*/
- TrackOutput getTrackOutput(int trackId);
+ TrackOutput buildOutput(int trackId);
+
+ /**
+ * Invoked when all {@link TrackOutput}s have been built, meaning {@link #buildOutput(int)}
+ * will not be invoked again.
+ */
+ void allOutputsBuilt();
}
@@ -46,6 +52,12 @@ public interface HlsExtractor {
*/
public interface TrackOutput {
+ boolean hasFormat();
+
+ void setFormat(MediaFormat format);
+
+ boolean isWritingSample();
+
int appendData(DataSource dataSource, int length) throws IOException;
void appendData(ParsableByteArray data, int length);
@@ -54,42 +66,14 @@ public interface HlsExtractor {
void commitSample(int flags, int offset, byte[] encryptionKey);
- boolean isWritingSample();
-
}
/**
* Initializes the extractor.
*
- * @param output An {@link ExtractorOutput} to which extracted data should be output.
+ * @param output A {@link TrackOutputBuilder} to which extracted data should be output.
*/
- void init(ExtractorOutput output);
-
- /**
- * Whether the extractor is prepared.
- *
- * @return True if the extractor is prepared. False otherwise.
- */
- boolean isPrepared();
-
- /**
- * Gets the number of available tracks.
- *
- * This method should only be called after the extractor has been prepared.
- *
- * @return The number of available tracks.
- */
- int getTrackCount();
-
- /**
- * Gets the format of the specified track.
- *
- * This method must only be called after the extractor has been prepared.
- *
- * @param track The track index.
- * @return The corresponding format.
- */
- MediaFormat getFormat(int track);
+ void init(TrackOutputBuilder output);
/**
* Reads up to a single TS packet.
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractorWrapper.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractorWrapper.java
index b44626722f..40bc8f7280 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractorWrapper.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/HlsExtractorWrapper.java
@@ -29,15 +29,17 @@ import java.io.IOException;
/**
* Wraps a {@link HlsExtractor}, adding functionality to enable reading of the extracted samples.
*/
-public final class HlsExtractorWrapper implements HlsExtractor.ExtractorOutput {
+public final class HlsExtractorWrapper implements HlsExtractor.TrackOutputBuilder {
private final BufferPool bufferPool;
private final HlsExtractor extractor;
+ private final SparseArray sampleQueues;
private final boolean shouldSpliceIn;
- private SparseArray sampleQueues;
+ private volatile boolean outputsBuilt;
// Accessed only by the consuming thread.
+ private boolean prepared;
private boolean spliceConfigured;
public HlsExtractorWrapper(BufferPool bufferPool, HlsExtractor extractor,
@@ -88,7 +90,7 @@ public final class HlsExtractorWrapper implements HlsExtractor.ExtractorOutput {
* @return The number of available tracks.
*/
public int getTrackCount() {
- return extractor.getTrackCount();
+ return sampleQueues.size();
}
/**
@@ -100,7 +102,7 @@ public final class HlsExtractorWrapper implements HlsExtractor.ExtractorOutput {
* @return The corresponding format.
*/
public MediaFormat getFormat(int track) {
- return extractor.getFormat(track);
+ return sampleQueues.valueAt(track).getFormat();
}
/**
@@ -109,7 +111,15 @@ public final class HlsExtractorWrapper implements HlsExtractor.ExtractorOutput {
* @return True if the extractor is prepared. False otherwise.
*/
public boolean isPrepared() {
- return extractor.isPrepared();
+ if (!prepared && outputsBuilt) {
+ for (int i = 0; i < sampleQueues.size(); i++) {
+ if (!sampleQueues.valueAt(i).hasFormat()) {
+ return false;
+ }
+ }
+ prepared = true;
+ }
+ return prepared;
}
/**
@@ -186,13 +196,15 @@ public final class HlsExtractorWrapper implements HlsExtractor.ExtractorOutput {
// ExtractorOutput implementation.
@Override
- public TrackOutput getTrackOutput(int id) {
- SampleQueue sampleQueue = sampleQueues.get(id);
- if (sampleQueue == null) {
- sampleQueue = new SampleQueue(bufferPool);
- sampleQueues.put(id, sampleQueue);
- }
+ public TrackOutput buildOutput(int id) {
+ SampleQueue sampleQueue = new SampleQueue(bufferPool);
+ sampleQueues.put(id, sampleQueue);
return sampleQueue;
}
+ @Override
+ public void allOutputsBuilt() {
+ this.outputsBuilt = true;
+ }
+
}
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/Id3Reader.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/Id3Reader.java
index a76bcdbaa4..d48aa6e56d 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/Id3Reader.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/Id3Reader.java
@@ -27,7 +27,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
public Id3Reader(TrackOutput output) {
super(output);
- setFormat(MediaFormat.createId3Format());
+ output.setFormat(MediaFormat.createId3Format());
}
@Override
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/SampleQueue.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/SampleQueue.java
index 5e9bd7950f..4f78f494bb 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/SampleQueue.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/SampleQueue.java
@@ -16,6 +16,7 @@
package com.google.android.exoplayer.hls.parser;
import com.google.android.exoplayer.C;
+import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.hls.parser.HlsExtractor.TrackOutput;
import com.google.android.exoplayer.upstream.BufferPool;
@@ -44,6 +45,7 @@ public final class SampleQueue implements TrackOutput {
// Accessed by both the loading and consuming threads.
private volatile long largestParsedTimestampUs;
+ private volatile MediaFormat format;
public SampleQueue(BufferPool bufferPool) {
rollingBuffer = new RollingSampleBuffer(bufferPool);
@@ -60,6 +62,10 @@ public final class SampleQueue implements TrackOutput {
// Called by the consuming thread.
+ public MediaFormat getFormat() {
+ return format;
+ }
+
public long getLargestParsedTimestampUs() {
return largestParsedTimestampUs;
}
@@ -162,6 +168,16 @@ public final class SampleQueue implements TrackOutput {
// TrackOutput implementation. Called by the loading thread.
+ @Override
+ public boolean hasFormat() {
+ return format != null;
+ }
+
+ @Override
+ public void setFormat(MediaFormat format) {
+ this.format = format;
+ }
+
@Override
public int appendData(DataSource dataSource, int length) throws IOException {
return rollingBuffer.appendData(dataSource, length);
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/SeiReader.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/SeiReader.java
index 2bf48be730..979bc44d5f 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/SeiReader.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/SeiReader.java
@@ -31,7 +31,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
public SeiReader(TrackOutput output) {
super(output);
- setFormat(MediaFormat.createEia608Format());
+ output.setFormat(MediaFormat.createEia608Format());
}
@Override
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/parser/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer/hls/parser/TsExtractor.java
index 15eae10354..4a4417270a 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/parser/TsExtractor.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/parser/TsExtractor.java
@@ -16,9 +16,7 @@
package com.google.android.exoplayer.hls.parser;
import com.google.android.exoplayer.C;
-import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.upstream.DataSource;
-import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.ParsableBitArray;
import com.google.android.exoplayer.util.ParsableByteArray;
@@ -52,14 +50,11 @@ public final class TsExtractor implements HlsExtractor {
private final ParsableBitArray tsScratch;
// Accessed only by the loading thread.
- private ExtractorOutput output;
+ private TrackOutputBuilder output;
private int tsPacketBytesRead;
private long timestampOffsetUs;
private long lastPts;
- // Accessed by both the loading and consuming threads.
- private volatile boolean prepared;
-
public TsExtractor(long firstSampleTimestamp) {
this.firstSampleTimestamp = firstSampleTimestamp;
tsScratch = new ParsableBitArray(new byte[3]);
@@ -71,40 +66,10 @@ public final class TsExtractor implements HlsExtractor {
}
@Override
- public void init(ExtractorOutput output) {
+ public void init(TrackOutputBuilder output) {
this.output = output;
}
- @Override
- public int getTrackCount() {
- Assertions.checkState(prepared);
- return streamReaders.size();
- }
-
- @Override
- public MediaFormat getFormat(int track) {
- Assertions.checkState(prepared);
- return streamReaders.valueAt(track).getFormat();
- }
-
- @Override
- public boolean isPrepared() {
- return prepared;
- }
-
- private boolean checkPrepared() {
- int pesPayloadReaderCount = streamReaders.size();
- if (pesPayloadReaderCount == 0) {
- return false;
- }
- for (int i = 0; i < pesPayloadReaderCount; i++) {
- if (!streamReaders.valueAt(i).hasFormat()) {
- return false;
- }
- }
- return true;
- }
-
@Override
public int read(DataSource dataSource) throws IOException {
int bytesRead = dataSource.read(tsPacketBuffer.data, tsPacketBytesRead,
@@ -153,10 +118,6 @@ public final class TsExtractor implements HlsExtractor {
}
}
- if (!prepared) {
- prepared = checkPrepared();
- }
-
return bytesRead;
}
@@ -193,7 +154,7 @@ public final class TsExtractor implements HlsExtractor {
private abstract static class TsPayloadReader {
public abstract void consume(ParsableByteArray data, boolean payloadUnitStartIndicator,
- ExtractorOutput output);
+ TrackOutputBuilder output);
}
@@ -210,7 +171,7 @@ public final class TsExtractor implements HlsExtractor {
@Override
public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator,
- ExtractorOutput output) {
+ TrackOutputBuilder output) {
// Skip pointer.
if (payloadUnitStartIndicator) {
int pointerField = data.readUnsignedByte();
@@ -250,7 +211,7 @@ public final class TsExtractor implements HlsExtractor {
@Override
public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator,
- ExtractorOutput output) {
+ TrackOutputBuilder output) {
// Skip pointer.
if (payloadUnitStartIndicator) {
int pointerField = data.readUnsignedByte();
@@ -294,16 +255,16 @@ public final class TsExtractor implements HlsExtractor {
ElementaryStreamReader pesPayloadReader = null;
switch (streamType) {
case TS_STREAM_TYPE_AAC:
- pesPayloadReader = new AdtsReader(output.getTrackOutput(TS_STREAM_TYPE_AAC));
+ pesPayloadReader = new AdtsReader(output.buildOutput(TS_STREAM_TYPE_AAC));
break;
case TS_STREAM_TYPE_H264:
- SeiReader seiReader = new SeiReader(output.getTrackOutput(TS_STREAM_TYPE_EIA608));
+ SeiReader seiReader = new SeiReader(output.buildOutput(TS_STREAM_TYPE_EIA608));
streamReaders.put(TS_STREAM_TYPE_EIA608, seiReader);
- pesPayloadReader = new H264Reader(output.getTrackOutput(TS_STREAM_TYPE_H264),
+ pesPayloadReader = new H264Reader(output.buildOutput(TS_STREAM_TYPE_H264),
seiReader);
break;
case TS_STREAM_TYPE_ID3:
- pesPayloadReader = new Id3Reader(output.getTrackOutput(TS_STREAM_TYPE_ID3));
+ pesPayloadReader = new Id3Reader(output.buildOutput(TS_STREAM_TYPE_ID3));
break;
}
@@ -313,7 +274,7 @@ public final class TsExtractor implements HlsExtractor {
}
}
- // Skip CRC_32.
+ output.allOutputsBuilt();
}
}
@@ -353,7 +314,7 @@ public final class TsExtractor implements HlsExtractor {
@Override
public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator,
- ExtractorOutput output) {
+ TrackOutputBuilder output) {
if (payloadUnitStartIndicator) {
switch (state) {
case STATE_FINDING_HEADER: