mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Further simplify HlsExtractor interface.
- Move to builder naming. - Propagate formats to the TrackOutput instances, rather than having them be read through the Extractor. There was actually some weird indexing going on here before (which happened to work, but wasn't well defined).
This commit is contained in:
parent
04cead415a
commit
1111dd73a0
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
|
@ -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<SampleQueue> sampleQueues;
|
||||
private final boolean shouldSpliceIn;
|
||||
|
||||
private SparseArray<SampleQueue> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user