Commit 5ce210e3 by olly Committed by Oliver Woodman

Propagate format information through RollingSampleBuffer.

At this point the only reason preventing the chunk package
from using RollingSampleBuffer directly, rather than
DefaultTrackOutput, is that the latter maintains the largest
parsed timestamp. This will be pushed inside the former in
the next CL. Following that, splicing logic will also be
pushed inside of RollingSampleBuffer, and HLS will be moved
over to using a single RollingSampleBuffer per track, with
the splicing done inline.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=120206658
parent febf86c5
...@@ -28,13 +28,6 @@ import com.google.android.exoplayer.upstream.DataSpec; ...@@ -28,13 +28,6 @@ import com.google.android.exoplayer.upstream.DataSpec;
*/ */
public abstract class BaseMediaChunk extends MediaChunk { public abstract class BaseMediaChunk extends MediaChunk {
/**
* Whether {@link #getSampleFormat()} and {@link #getDrmInitData()} can be called at any time to
* obtain the chunk's sample format and drm initialization data. If false, these methods are only
* guaranteed to return correct data after the first sample data has been output from the chunk.
*/
public final boolean isSampleFormatFinal;
private DefaultTrackOutput trackOutput; private DefaultTrackOutput trackOutput;
private int firstSampleIndex; private int firstSampleIndex;
...@@ -46,15 +39,10 @@ public abstract class BaseMediaChunk extends MediaChunk { ...@@ -46,15 +39,10 @@ public abstract class BaseMediaChunk extends MediaChunk {
* @param startTimeUs The start time of the media contained by the chunk, in microseconds. * @param startTimeUs The start time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds. * @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param chunkIndex The index of the chunk. * @param chunkIndex The index of the chunk.
* @param isSampleFormatFinal True if {@link #getSampleFormat()} and {@link #getDrmInitData()} can
* be called at any time to obtain the sample format and drm initialization data. False if
* these methods are only guaranteed to return correct data after the first sample data has
* been output from the chunk.
*/ */
public BaseMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format, public BaseMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format,
long startTimeUs, long endTimeUs, int chunkIndex, boolean isSampleFormatFinal) { long startTimeUs, long endTimeUs, int chunkIndex) {
super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex); super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex);
this.isSampleFormatFinal = isSampleFormatFinal;
} }
/** /**
...@@ -77,20 +65,7 @@ public abstract class BaseMediaChunk extends MediaChunk { ...@@ -77,20 +65,7 @@ public abstract class BaseMediaChunk extends MediaChunk {
} }
/** /**
* Gets the {@link Format} of the samples in the chunk.
* <p>
* See {@link #isSampleFormatFinal} for information about when this method is guaranteed to return
* correct data.
*
* @return The {@link Format} of the samples in the chunk.
*/
public abstract Format getSampleFormat();
/**
* Gets the {@link DrmInitData} corresponding to the chunk. * Gets the {@link DrmInitData} corresponding to the chunk.
* <p>
* See {@link #isSampleFormatFinal} for information about when this method is guaranteed to return
* correct data.
* *
* @return The {@link DrmInitData} corresponding to this chunk. * @return The {@link DrmInitData} corresponding to this chunk.
*/ */
......
...@@ -50,11 +50,6 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput ...@@ -50,11 +50,6 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
*/ */
void drmInitData(DrmInitData drmInitData); void drmInitData(DrmInitData drmInitData);
/**
* @see TrackOutput#format(Format)
*/
void format(Format format);
} }
private final Extractor extractor; private final Extractor extractor;
...@@ -132,8 +127,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput ...@@ -132,8 +127,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
@Override @Override
public void format(Format format) { public void format(Format format) {
// Redirect the format to the metadata output. The track output doesn't need it. trackOutput.format(format);
metadataOutput.format(format);
} }
@Override @Override
......
...@@ -300,14 +300,12 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call ...@@ -300,14 +300,12 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call
downstreamFormat = currentChunk.format; downstreamFormat = currentChunk.format;
} }
if (haveSamples || currentChunk.isSampleFormatFinal) { Format sampleFormat = sampleQueue.getDownstreamFormat();
Format sampleFormat = currentChunk.getSampleFormat(); if (sampleFormat != null && !sampleFormat.equals(downstreamSampleFormat)) {
if (!sampleFormat.equals(downstreamSampleFormat)) { formatHolder.format = sampleFormat;
formatHolder.format = sampleFormat; formatHolder.drmInitData = currentChunk.getDrmInitData();
formatHolder.drmInitData = currentChunk.getDrmInitData(); downstreamSampleFormat = sampleFormat;
downstreamSampleFormat = sampleFormat; return FORMAT_READ;
return FORMAT_READ;
}
} }
if (!haveSamples) { if (!haveSamples) {
......
...@@ -60,11 +60,10 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -60,11 +60,10 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format, public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format,
long startTimeUs, long endTimeUs, int chunkIndex, long sampleOffsetUs, long startTimeUs, long endTimeUs, int chunkIndex, long sampleOffsetUs,
ChunkExtractorWrapper extractorWrapper, Format sampleFormat, DrmInitData drmInitData) { ChunkExtractorWrapper extractorWrapper, Format sampleFormat, DrmInitData drmInitData) {
super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex);
sampleFormat != null);
this.extractorWrapper = extractorWrapper; this.extractorWrapper = extractorWrapper;
this.sampleOffsetUs = sampleOffsetUs; this.sampleOffsetUs = sampleOffsetUs;
this.sampleFormat = getAdjustedSampleFormat(sampleFormat, sampleOffsetUs); this.sampleFormat = sampleFormat;
this.drmInitData = drmInitData; this.drmInitData = drmInitData;
} }
...@@ -74,11 +73,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -74,11 +73,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
} }
@Override @Override
public final Format getSampleFormat() {
return sampleFormat;
}
@Override
public final DrmInitData getDrmInitData() { public final DrmInitData getDrmInitData() {
return drmInitData; return drmInitData;
} }
...@@ -95,11 +89,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -95,11 +89,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
this.drmInitData = drmInitData; this.drmInitData = drmInitData;
} }
@Override
public final void format(Format format) {
this.sampleFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
}
// Loadable implementation. // Loadable implementation.
@Override @Override
...@@ -123,7 +112,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -123,7 +112,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
if (bytesLoaded == 0) { if (bytesLoaded == 0) {
// Set the target to ourselves. // Set the target to ourselves.
DefaultTrackOutput trackOutput = getTrackOutput(); DefaultTrackOutput trackOutput = getTrackOutput();
trackOutput.setSampleOffsetUs(sampleOffsetUs); trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs);
extractorWrapper.init(this, trackOutput); extractorWrapper.init(this, trackOutput);
} }
// Load and parse the sample data. // Load and parse the sample data.
...@@ -140,16 +129,4 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe ...@@ -140,16 +129,4 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe
} }
} }
// Private methods.
private static Format getAdjustedSampleFormat(Format format, long sampleOffsetUs) {
if (format == null) {
return null;
}
if (sampleOffsetUs != 0 && format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
format = format.copyWithSubsampleOffsetUs(format.subsampleOffsetUs + sampleOffsetUs);
}
return format;
}
} }
...@@ -87,15 +87,6 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad ...@@ -87,15 +87,6 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad
} }
/** /**
* True if a {@link SeekMap} was parsed from the chunk. False otherwise.
* <p>
* Should be called after loading has completed.
*/
public boolean hasSeekMap() {
return seekMap != null;
}
/**
* Returns a {@link SeekMap} parsed from the chunk, or null. * Returns a {@link SeekMap} parsed from the chunk, or null.
* <p> * <p>
* Should be called after loading has completed. * Should be called after loading has completed.
...@@ -116,13 +107,13 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad ...@@ -116,13 +107,13 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad
this.drmInitData = drmInitData; this.drmInitData = drmInitData;
} }
// TrackOutput implementation.
@Override @Override
public void format(Format format) { public void format(Format format) {
this.sampleFormat = format; this.sampleFormat = format;
} }
// TrackOutput implementation.
@Override @Override
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException { throws IOException, InterruptedException {
......
...@@ -19,8 +19,8 @@ import com.google.android.exoplayer.C; ...@@ -19,8 +19,8 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.DefaultExtractorInput; import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
...@@ -53,7 +53,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { ...@@ -53,7 +53,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger,
Format format, long startTimeUs, long endTimeUs, int chunkIndex, Format sampleFormat, Format format, long startTimeUs, long endTimeUs, int chunkIndex, Format sampleFormat,
DrmInitData sampleDrmInitData) { DrmInitData sampleDrmInitData) {
super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, true); super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex);
this.sampleFormat = sampleFormat; this.sampleFormat = sampleFormat;
this.sampleDrmInitData = sampleDrmInitData; this.sampleDrmInitData = sampleDrmInitData;
} }
...@@ -64,11 +64,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { ...@@ -64,11 +64,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
} }
@Override @Override
public Format getSampleFormat() {
return sampleFormat;
}
@Override
public DrmInitData getDrmInitData() { public DrmInitData getDrmInitData() {
return sampleDrmInitData; return sampleDrmInitData;
} }
...@@ -96,7 +91,8 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { ...@@ -96,7 +91,8 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
length += bytesLoaded; length += bytesLoaded;
} }
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length); ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
TrackOutput trackOutput = getTrackOutput(); DefaultTrackOutput trackOutput = getTrackOutput();
trackOutput.formatWithOffset(sampleFormat, 0);
// Load the sample data. // Load the sample data.
int result = 0; int result = 0;
while (result != C.RESULT_END_OF_INPUT) { while (result != C.RESULT_END_OF_INPUT) {
......
...@@ -38,6 +38,7 @@ import com.google.android.exoplayer.dash.mpd.RangedUri; ...@@ -38,6 +38,7 @@ import com.google.android.exoplayer.dash.mpd.RangedUri;
import com.google.android.exoplayer.dash.mpd.Representation; import com.google.android.exoplayer.dash.mpd.Representation;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
...@@ -296,10 +297,12 @@ public class DashChunkSource implements ChunkSource { ...@@ -296,10 +297,12 @@ public class DashChunkSource implements ChunkSource {
// The null check avoids overwriting an index obtained from the manifest with one obtained // The null check avoids overwriting an index obtained from the manifest with one obtained
// from the stream. If the manifest defines an index then the stream shouldn't, but in cases // from the stream. If the manifest defines an index then the stream shouldn't, but in cases
// where it does we should ignore it. // where it does we should ignore it.
if (representationHolder.segmentIndex == null && initializationChunk.hasSeekMap()) { if (representationHolder.segmentIndex == null) {
representationHolder.segmentIndex = new DashWrappingSegmentIndex( SeekMap seekMap = initializationChunk.getSeekMap();
(ChunkIndex) initializationChunk.getSeekMap(), if (seekMap != null) {
initializationChunk.dataSpec.uri.toString()); representationHolder.segmentIndex = new DashWrappingSegmentIndex((ChunkIndex) seekMap,
initializationChunk.dataSpec.uri.toString());
}
} }
// The null check avoids overwriting drmInitData obtained from the manifest with drmInitData // The null check avoids overwriting drmInitData obtained from the manifest with drmInitData
// obtained from the stream, as per DASH IF Interoperability Recommendations V3.0, 7.5.3. // obtained from the stream, as per DASH IF Interoperability Recommendations V3.0, 7.5.3.
......
...@@ -92,13 +92,20 @@ public final class DefaultTrackOutput implements TrackOutput { ...@@ -92,13 +92,20 @@ public final class DefaultTrackOutput implements TrackOutput {
} }
/** /**
* The format most recently received by the output, or null if a format has yet to be received. * Returns the current upstream {@link Format}.
*/ */
public Format getFormat() { public Format getUpstreamFormat() {
return rollingBuffer.getUpstreamFormat(); return rollingBuffer.getUpstreamFormat();
} }
/** /**
* Returns the current downstream {@link Format}.
*/
public Format getDownstreamFormat() {
return rollingBuffer.getDownstreamFormat();
}
/**
* The largest timestamp of any sample received by the output, or {@link Long#MIN_VALUE} if a * The largest timestamp of any sample received by the output, or {@link Long#MIN_VALUE} if a
* sample has yet to be received. * sample has yet to be received.
*/ */
...@@ -210,13 +217,16 @@ public final class DefaultTrackOutput implements TrackOutput { ...@@ -210,13 +217,16 @@ public final class DefaultTrackOutput implements TrackOutput {
// Called by the loading thread. // Called by the loading thread.
/** /**
* Sets an offset that will be added to the timestamps passed to * Like {@link #format(Format)}, but with an offset that will be added to the timestamps of
* {@link #sampleMetadata(long, int, int, int, byte[])}. * samples subsequently queued to the buffer. The offset is also used to adjust
* {@link Format#subsampleOffsetUs} for both the {@link Format} passed and those subsequently
* passed to {@link #format(Format)}.
* *
* @param format The format.
* @param sampleOffsetUs The offset in microseconds. * @param sampleOffsetUs The offset in microseconds.
*/ */
public void setSampleOffsetUs(long sampleOffsetUs) { public void formatWithOffset(Format format, long sampleOffsetUs) {
rollingBuffer.setSampleOffsetUs(sampleOffsetUs); rollingBuffer.formatWithOffset(format, sampleOffsetUs);
} }
@Override @Override
......
...@@ -343,7 +343,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -343,7 +343,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
pendingMediaFormat = new boolean[trackCount]; pendingMediaFormat = new boolean[trackCount];
durationUs = seekMap.getDurationUs(); durationUs = seekMap.getDurationUs();
for (int i = 0; i < trackCount; i++) { for (int i = 0; i < trackCount; i++) {
trackArray[i] = new TrackGroup(sampleQueues.valueAt(i).getFormat()); trackArray[i] = new TrackGroup(sampleQueues.valueAt(i).getUpstreamFormat());
} }
tracks = new TrackGroupArray(trackArray); tracks = new TrackGroupArray(trackArray);
if (minLoadableRetryCount == MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA && !seekMap.isSeekable() if (minLoadableRetryCount == MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA && !seekMap.isSeekable()
...@@ -476,7 +476,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -476,7 +476,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
DefaultTrackOutput sampleQueue = sampleQueues.valueAt(track); DefaultTrackOutput sampleQueue = sampleQueues.valueAt(track);
if (pendingMediaFormat[track]) { if (pendingMediaFormat[track]) {
formatHolder.format = sampleQueue.getFormat(); formatHolder.format = sampleQueue.getUpstreamFormat();
formatHolder.drmInitData = drmInitData; formatHolder.drmInitData = drmInitData;
pendingMediaFormat[track] = false; pendingMediaFormat[track] = false;
return TrackStream.FORMAT_READ; return TrackStream.FORMAT_READ;
...@@ -649,7 +649,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -649,7 +649,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private boolean haveFormatsForAllTracks() { private boolean haveFormatsForAllTracks() {
for (int i = 0; i < sampleQueues.size(); i++) { for (int i = 0; i < sampleQueues.size(); i++) {
if (sampleQueues.valueAt(i).getFormat() == null) { if (sampleQueues.valueAt(i).getUpstreamFormat() == null) {
return false; return false;
} }
} }
......
...@@ -137,13 +137,21 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -137,13 +137,21 @@ import java.util.concurrent.LinkedBlockingDeque;
} }
/** /**
* Returns the current upstream format. * Returns the current upstream {@link Format}.
*/ */
public Format getUpstreamFormat() { public Format getUpstreamFormat() {
return upstreamFormat; return upstreamFormat;
} }
/** /**
* Returns the current downstream {@link Format}.
*/
public Format getDownstreamFormat() {
Format nextSampleFormat = infoQueue.peekFormat();
return nextSampleFormat != null ? nextSampleFormat : upstreamFormat;
}
/**
* Fills {@code buffer} with information about the current sample, but does not write its data. * Fills {@code buffer} with information about the current sample, but does not write its data.
* <p> * <p>
* Populates {@link DecoderInputBuffer#size}, {@link DecoderInputBuffer#timeUs} and the buffer * Populates {@link DecoderInputBuffer#size}, {@link DecoderInputBuffer#timeUs} and the buffer
...@@ -357,17 +365,22 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -357,17 +365,22 @@ import java.util.concurrent.LinkedBlockingDeque;
// Called by the loading thread. // Called by the loading thread.
/** /**
* Sets an offset that will be added to the timestamps of subsequently queued samples. * Like {@link #format(Format)}, but with an offset that will be added to the timestamps of
* samples subsequently queued to the buffer. The offset is also used to adjust
* {@link Format#subsampleOffsetUs} for both the {@link Format} passed and those subsequently
* passed to {@link #format(Format)}.
* *
* @param format The format.
* @param sampleOffsetUs The timestamp offset in microseconds. * @param sampleOffsetUs The timestamp offset in microseconds.
*/ */
public void setSampleOffsetUs(long sampleOffsetUs) { public void formatWithOffset(Format format, long sampleOffsetUs) {
this.sampleOffsetUs = sampleOffsetUs; this.sampleOffsetUs = sampleOffsetUs;
upstreamFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
} }
@Override @Override
public void format(Format format) { public void format(Format format) {
upstreamFormat = format; upstreamFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
} }
@Override @Override
...@@ -403,7 +416,7 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -403,7 +416,7 @@ import java.util.concurrent.LinkedBlockingDeque;
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
timeUs += sampleOffsetUs; timeUs += sampleOffsetUs;
long absoluteOffset = totalBytesWritten - size - offset; long absoluteOffset = totalBytesWritten - size - offset;
infoQueue.commitSample(timeUs, flags, absoluteOffset, size, encryptionKey); infoQueue.commitSample(timeUs, flags, absoluteOffset, size, encryptionKey, upstreamFormat);
} }
/** /**
...@@ -420,6 +433,23 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -420,6 +433,23 @@ import java.util.concurrent.LinkedBlockingDeque;
} }
/** /**
* Adjusts a {@link Format} to incorporate a sample offset into {@link Format#subsampleOffsetUs}.
*
* @param format The {@link Format} to adjust.
* @param sampleOffsetUs The offset to apply.
* @return The adjusted {@link Format}.
*/
private static Format getAdjustedSampleFormat(Format format, long sampleOffsetUs) {
if (format == null) {
return null;
}
if (sampleOffsetUs != 0 && format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
format = format.copyWithSubsampleOffsetUs(format.subsampleOffsetUs + sampleOffsetUs);
}
return format;
}
/**
* Holds information about the samples in the rolling buffer. * Holds information about the samples in the rolling buffer.
*/ */
private static final class InfoQueue { private static final class InfoQueue {
...@@ -433,6 +463,7 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -433,6 +463,7 @@ import java.util.concurrent.LinkedBlockingDeque;
private int[] flags; private int[] flags;
private long[] timesUs; private long[] timesUs;
private byte[][] encryptionKeys; private byte[][] encryptionKeys;
private Format[] formats;
private int queueSize; private int queueSize;
private int absoluteReadIndex; private int absoluteReadIndex;
...@@ -446,6 +477,7 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -446,6 +477,7 @@ import java.util.concurrent.LinkedBlockingDeque;
flags = new int[capacity]; flags = new int[capacity];
sizes = new int[capacity]; sizes = new int[capacity];
encryptionKeys = new byte[capacity][]; encryptionKeys = new byte[capacity][];
formats = new Format[capacity];
} }
// Called by the consuming thread, but only when there is no loading thread. // Called by the consuming thread, but only when there is no loading thread.
...@@ -501,6 +533,16 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -501,6 +533,16 @@ import java.util.concurrent.LinkedBlockingDeque;
} }
/** /**
* Returns the {@link Format} of the next sample, or null of the queue is empty.
*/
public synchronized Format peekFormat() {
if (queueSize == 0) {
return null;
}
return formats[relativeReadIndex];
}
/**
* Fills {@code buffer} with information about the current sample, but does not write its data. * Fills {@code buffer} with information about the current sample, but does not write its data.
* The absolute position of the sample's data in the rolling buffer is stored in * The absolute position of the sample's data in the rolling buffer is stored in
* {@code extrasHolder}. * {@code extrasHolder}.
...@@ -609,12 +651,13 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -609,12 +651,13 @@ import java.util.concurrent.LinkedBlockingDeque;
// Called by the loading thread. // Called by the loading thread.
public synchronized void commitSample(long timeUs, int sampleFlags, long offset, int size, public synchronized void commitSample(long timeUs, int sampleFlags, long offset, int size,
byte[] encryptionKey) { byte[] encryptionKey, Format format) {
timesUs[relativeWriteIndex] = timeUs; timesUs[relativeWriteIndex] = timeUs;
offsets[relativeWriteIndex] = offset; offsets[relativeWriteIndex] = offset;
sizes[relativeWriteIndex] = size; sizes[relativeWriteIndex] = size;
flags[relativeWriteIndex] = sampleFlags; flags[relativeWriteIndex] = sampleFlags;
encryptionKeys[relativeWriteIndex] = encryptionKey; encryptionKeys[relativeWriteIndex] = encryptionKey;
formats[relativeWriteIndex] = format;
// Increment the write index. // Increment the write index.
queueSize++; queueSize++;
if (queueSize == capacity) { if (queueSize == capacity) {
...@@ -625,23 +668,27 @@ import java.util.concurrent.LinkedBlockingDeque; ...@@ -625,23 +668,27 @@ import java.util.concurrent.LinkedBlockingDeque;
int[] newFlags = new int[newCapacity]; int[] newFlags = new int[newCapacity];
int[] newSizes = new int[newCapacity]; int[] newSizes = new int[newCapacity];
byte[][] newEncryptionKeys = new byte[newCapacity][]; byte[][] newEncryptionKeys = new byte[newCapacity][];
Format[] newFormats = new Format[newCapacity];
int beforeWrap = capacity - relativeReadIndex; int beforeWrap = capacity - relativeReadIndex;
System.arraycopy(offsets, relativeReadIndex, newOffsets, 0, beforeWrap); System.arraycopy(offsets, relativeReadIndex, newOffsets, 0, beforeWrap);
System.arraycopy(timesUs, relativeReadIndex, newTimesUs, 0, beforeWrap); System.arraycopy(timesUs, relativeReadIndex, newTimesUs, 0, beforeWrap);
System.arraycopy(flags, relativeReadIndex, newFlags, 0, beforeWrap); System.arraycopy(flags, relativeReadIndex, newFlags, 0, beforeWrap);
System.arraycopy(sizes, relativeReadIndex, newSizes, 0, beforeWrap); System.arraycopy(sizes, relativeReadIndex, newSizes, 0, beforeWrap);
System.arraycopy(encryptionKeys, relativeReadIndex, newEncryptionKeys, 0, beforeWrap); System.arraycopy(encryptionKeys, relativeReadIndex, newEncryptionKeys, 0, beforeWrap);
System.arraycopy(formats, relativeReadIndex, newFormats, 0, beforeWrap);
int afterWrap = relativeReadIndex; int afterWrap = relativeReadIndex;
System.arraycopy(offsets, 0, newOffsets, beforeWrap, afterWrap); System.arraycopy(offsets, 0, newOffsets, beforeWrap, afterWrap);
System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap); System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap);
System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap); System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap);
System.arraycopy(sizes, 0, newSizes, beforeWrap, afterWrap); System.arraycopy(sizes, 0, newSizes, beforeWrap, afterWrap);
System.arraycopy(encryptionKeys, 0, newEncryptionKeys, beforeWrap, afterWrap); System.arraycopy(encryptionKeys, 0, newEncryptionKeys, beforeWrap, afterWrap);
System.arraycopy(formats, 0, newFormats, beforeWrap, afterWrap);
offsets = newOffsets; offsets = newOffsets;
timesUs = newTimesUs; timesUs = newTimesUs;
flags = newFlags; flags = newFlags;
sizes = newSizes; sizes = newSizes;
encryptionKeys = newEncryptionKeys; encryptionKeys = newEncryptionKeys;
formats = newFormats;
relativeReadIndex = 0; relativeReadIndex = 0;
relativeWriteIndex = capacity; relativeWriteIndex = capacity;
queueSize = capacity; queueSize = capacity;
......
...@@ -44,7 +44,6 @@ public final class HlsExtractorWrapper implements ExtractorOutput { ...@@ -44,7 +44,6 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
private final SparseArray<DefaultTrackOutput> sampleQueues; private final SparseArray<DefaultTrackOutput> sampleQueues;
private final boolean shouldSpliceIn; private final boolean shouldSpliceIn;
private Format[] sampleFormats;
private Allocator allocator; private Allocator allocator;
private volatile boolean tracksBuilt; private volatile boolean tracksBuilt;
...@@ -81,15 +80,11 @@ public final class HlsExtractorWrapper implements ExtractorOutput { ...@@ -81,15 +80,11 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
public boolean isPrepared() { public boolean isPrepared() {
if (!prepared && tracksBuilt) { if (!prepared && tracksBuilt) {
for (int i = 0; i < sampleQueues.size(); i++) { for (int i = 0; i < sampleQueues.size(); i++) {
if (sampleQueues.valueAt(i).getFormat() == null) { if (sampleQueues.valueAt(i).getUpstreamFormat() == null) {
return false; return false;
} }
} }
prepared = true; prepared = true;
sampleFormats = new Format[sampleQueues.size()];
for (int i = 0; i < sampleFormats.length; i++) {
sampleFormats[i] = sampleQueues.valueAt(i).getFormat();
}
} }
return prepared; return prepared;
} }
...@@ -173,7 +168,7 @@ public final class HlsExtractorWrapper implements ExtractorOutput { ...@@ -173,7 +168,7 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
*/ */
public Format getSampleFormat(int track) { public Format getSampleFormat(int track) {
Assertions.checkState(isPrepared()); Assertions.checkState(isPrepared());
return sampleFormats[track]; return sampleQueues.valueAt(track).getUpstreamFormat();
} }
/** /**
......
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