diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java index 609eb9cb4e..3483ed68d5 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java @@ -28,13 +28,6 @@ import com.google.android.exoplayer.upstream.DataSpec; */ 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 int firstSampleIndex; @@ -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 endTimeUs The end time of the media contained by the chunk, in microseconds. * @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, - long startTimeUs, long endTimeUs, int chunkIndex, boolean isSampleFormatFinal) { + long startTimeUs, long endTimeUs, int chunkIndex) { super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex); - this.isSampleFormatFinal = isSampleFormatFinal; } /** @@ -76,21 +64,8 @@ public abstract class BaseMediaChunk extends MediaChunk { return firstSampleIndex; } - /** - * Gets the {@link Format} of the samples in the chunk. - *
- * 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. - *
- * See {@link #isSampleFormatFinal} for information about when this method is guaranteed to return - * correct data. * * @return The {@link DrmInitData} corresponding to this chunk. */ diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java index ee2ec5b0c9..63850a16bc 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java @@ -50,11 +50,6 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput */ void drmInitData(DrmInitData drmInitData); - /** - * @see TrackOutput#format(Format) - */ - void format(Format format); - } private final Extractor extractor; @@ -132,8 +127,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput @Override public void format(Format format) { - // Redirect the format to the metadata output. The track output doesn't need it. - metadataOutput.format(format); + trackOutput.format(format); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java index 3fbd9d781e..d79d8cb302 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java @@ -300,14 +300,12 @@ public class ChunkSampleSource implements SampleSource, TrackStream, Loader.Call downstreamFormat = currentChunk.format; } - if (haveSamples || currentChunk.isSampleFormatFinal) { - Format sampleFormat = currentChunk.getSampleFormat(); - if (!sampleFormat.equals(downstreamSampleFormat)) { - formatHolder.format = sampleFormat; - formatHolder.drmInitData = currentChunk.getDrmInitData(); - downstreamSampleFormat = sampleFormat; - return FORMAT_READ; - } + Format sampleFormat = sampleQueue.getDownstreamFormat(); + if (sampleFormat != null && !sampleFormat.equals(downstreamSampleFormat)) { + formatHolder.format = sampleFormat; + formatHolder.drmInitData = currentChunk.getDrmInitData(); + downstreamSampleFormat = sampleFormat; + return FORMAT_READ; } if (!haveSamples) { diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java index 43be1784de..67d8da8d5f 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java @@ -60,11 +60,10 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format, long startTimeUs, long endTimeUs, int chunkIndex, long sampleOffsetUs, ChunkExtractorWrapper extractorWrapper, Format sampleFormat, DrmInitData drmInitData) { - super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, - sampleFormat != null); + super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex); this.extractorWrapper = extractorWrapper; this.sampleOffsetUs = sampleOffsetUs; - this.sampleFormat = getAdjustedSampleFormat(sampleFormat, sampleOffsetUs); + this.sampleFormat = sampleFormat; this.drmInitData = drmInitData; } @@ -73,11 +72,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe return bytesLoaded; } - @Override - public final Format getSampleFormat() { - return sampleFormat; - } - @Override public final DrmInitData getDrmInitData() { return drmInitData; @@ -95,11 +89,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe this.drmInitData = drmInitData; } - @Override - public final void format(Format format) { - this.sampleFormat = getAdjustedSampleFormat(format, sampleOffsetUs); - } - // Loadable implementation. @Override @@ -123,7 +112,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe if (bytesLoaded == 0) { // Set the target to ourselves. DefaultTrackOutput trackOutput = getTrackOutput(); - trackOutput.setSampleOffsetUs(sampleOffsetUs); + trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs); extractorWrapper.init(this, trackOutput); } // Load and parse the sample data. @@ -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; - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java index 63f66e2a69..303419617d 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java @@ -86,15 +86,6 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad return drmInitData; } - /** - * True if a {@link SeekMap} was parsed from the chunk. False otherwise. - *
- * Should be called after loading has completed. - */ - public boolean hasSeekMap() { - return seekMap != null; - } - /** * Returns a {@link SeekMap} parsed from the chunk, or null. *
@@ -116,13 +107,13 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad this.drmInitData = drmInitData; } + // TrackOutput implementation. + @Override public void format(Format format) { this.sampleFormat = format; } - // TrackOutput implementation. - @Override public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) throws IOException, InterruptedException { diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java index c93eecb722..82a3dc8696 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java @@ -19,8 +19,8 @@ import com.google.android.exoplayer.C; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.drm.DrmInitData; 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.TrackOutput; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.util.Util; @@ -53,7 +53,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format, long startTimeUs, long endTimeUs, int chunkIndex, Format sampleFormat, DrmInitData sampleDrmInitData) { - super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, true); + super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex); this.sampleFormat = sampleFormat; this.sampleDrmInitData = sampleDrmInitData; } @@ -63,11 +63,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { return bytesLoaded; } - @Override - public Format getSampleFormat() { - return sampleFormat; - } - @Override public DrmInitData getDrmInitData() { return sampleDrmInitData; @@ -96,7 +91,8 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { length += bytesLoaded; } ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length); - TrackOutput trackOutput = getTrackOutput(); + DefaultTrackOutput trackOutput = getTrackOutput(); + trackOutput.formatWithOffset(sampleFormat, 0); // Load the sample data. int result = 0; while (result != C.RESULT_END_OF_INPUT) { diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index 7a7fff2f91..99c44ca554 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -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.drm.DrmInitData; 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.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer.upstream.DataSource; @@ -296,10 +297,12 @@ public class DashChunkSource implements ChunkSource { // 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 // where it does we should ignore it. - if (representationHolder.segmentIndex == null && initializationChunk.hasSeekMap()) { - representationHolder.segmentIndex = new DashWrappingSegmentIndex( - (ChunkIndex) initializationChunk.getSeekMap(), - initializationChunk.dataSpec.uri.toString()); + if (representationHolder.segmentIndex == null) { + SeekMap seekMap = initializationChunk.getSeekMap(); + if (seekMap != null) { + representationHolder.segmentIndex = new DashWrappingSegmentIndex((ChunkIndex) seekMap, + initializationChunk.dataSpec.uri.toString()); + } } // 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. diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java b/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java index a7d1a8f86c..c3ef28c4d2 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java @@ -92,12 +92,19 @@ 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(); } + /** + * 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 * sample has yet to be received. @@ -210,13 +217,16 @@ public final class DefaultTrackOutput implements TrackOutput { // Called by the loading thread. /** - * Sets an offset that will be added to the timestamps passed to - * {@link #sampleMetadata(long, int, int, int, byte[])}. + * 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 offset in microseconds. */ - public void setSampleOffsetUs(long sampleOffsetUs) { - rollingBuffer.setSampleOffsetUs(sampleOffsetUs); + public void formatWithOffset(Format format, long sampleOffsetUs) { + rollingBuffer.formatWithOffset(format, sampleOffsetUs); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java index 4f415ce85e..378917e5de 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java @@ -343,7 +343,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu pendingMediaFormat = new boolean[trackCount]; durationUs = seekMap.getDurationUs(); 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); if (minLoadableRetryCount == MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA && !seekMap.isSeekable() @@ -476,7 +476,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu DefaultTrackOutput sampleQueue = sampleQueues.valueAt(track); if (pendingMediaFormat[track]) { - formatHolder.format = sampleQueue.getFormat(); + formatHolder.format = sampleQueue.getUpstreamFormat(); formatHolder.drmInitData = drmInitData; pendingMediaFormat[track] = false; return TrackStream.FORMAT_READ; @@ -649,7 +649,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu private boolean haveFormatsForAllTracks() { for (int i = 0; i < sampleQueues.size(); i++) { - if (sampleQueues.valueAt(i).getFormat() == null) { + if (sampleQueues.valueAt(i).getUpstreamFormat() == null) { return false; } } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/RollingSampleBuffer.java b/library/src/main/java/com/google/android/exoplayer/extractor/RollingSampleBuffer.java index 2c0786cfb1..2ac38e66d6 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/RollingSampleBuffer.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/RollingSampleBuffer.java @@ -137,12 +137,20 @@ import java.util.concurrent.LinkedBlockingDeque; } /** - * Returns the current upstream format. + * Returns the current upstream {@link Format}. */ public Format getUpstreamFormat() { 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. *
@@ -357,17 +365,22 @@ import java.util.concurrent.LinkedBlockingDeque;
// 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.
*/
- public void setSampleOffsetUs(long sampleOffsetUs) {
+ public void formatWithOffset(Format format, long sampleOffsetUs) {
this.sampleOffsetUs = sampleOffsetUs;
+ upstreamFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
}
@Override
public void format(Format format) {
- upstreamFormat = format;
+ upstreamFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
}
@Override
@@ -403,7 +416,7 @@ import java.util.concurrent.LinkedBlockingDeque;
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
timeUs += sampleOffsetUs;
long absoluteOffset = totalBytesWritten - size - offset;
- infoQueue.commitSample(timeUs, flags, absoluteOffset, size, encryptionKey);
+ infoQueue.commitSample(timeUs, flags, absoluteOffset, size, encryptionKey, upstreamFormat);
}
/**
@@ -419,6 +432,23 @@ import java.util.concurrent.LinkedBlockingDeque;
return Math.min(length, allocationLength - lastAllocationOffset);
}
+ /**
+ * 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.
*/
@@ -433,6 +463,7 @@ import java.util.concurrent.LinkedBlockingDeque;
private int[] flags;
private long[] timesUs;
private byte[][] encryptionKeys;
+ private Format[] formats;
private int queueSize;
private int absoluteReadIndex;
@@ -446,6 +477,7 @@ import java.util.concurrent.LinkedBlockingDeque;
flags = new int[capacity];
sizes = new int[capacity];
encryptionKeys = new byte[capacity][];
+ formats = new Format[capacity];
}
// Called by the consuming thread, but only when there is no loading thread.
@@ -500,6 +532,16 @@ import java.util.concurrent.LinkedBlockingDeque;
return absoluteReadIndex;
}
+ /**
+ * 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.
* The absolute position of the sample's data in the rolling buffer is stored in
@@ -609,12 +651,13 @@ import java.util.concurrent.LinkedBlockingDeque;
// Called by the loading thread.
public synchronized void commitSample(long timeUs, int sampleFlags, long offset, int size,
- byte[] encryptionKey) {
+ byte[] encryptionKey, Format format) {
timesUs[relativeWriteIndex] = timeUs;
offsets[relativeWriteIndex] = offset;
sizes[relativeWriteIndex] = size;
flags[relativeWriteIndex] = sampleFlags;
encryptionKeys[relativeWriteIndex] = encryptionKey;
+ formats[relativeWriteIndex] = format;
// Increment the write index.
queueSize++;
if (queueSize == capacity) {
@@ -625,23 +668,27 @@ import java.util.concurrent.LinkedBlockingDeque;
int[] newFlags = new int[newCapacity];
int[] newSizes = new int[newCapacity];
byte[][] newEncryptionKeys = new byte[newCapacity][];
+ Format[] newFormats = new Format[newCapacity];
int beforeWrap = capacity - relativeReadIndex;
System.arraycopy(offsets, relativeReadIndex, newOffsets, 0, beforeWrap);
System.arraycopy(timesUs, relativeReadIndex, newTimesUs, 0, beforeWrap);
System.arraycopy(flags, relativeReadIndex, newFlags, 0, beforeWrap);
System.arraycopy(sizes, relativeReadIndex, newSizes, 0, beforeWrap);
System.arraycopy(encryptionKeys, relativeReadIndex, newEncryptionKeys, 0, beforeWrap);
+ System.arraycopy(formats, relativeReadIndex, newFormats, 0, beforeWrap);
int afterWrap = relativeReadIndex;
System.arraycopy(offsets, 0, newOffsets, beforeWrap, afterWrap);
System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap);
System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap);
System.arraycopy(sizes, 0, newSizes, beforeWrap, afterWrap);
System.arraycopy(encryptionKeys, 0, newEncryptionKeys, beforeWrap, afterWrap);
+ System.arraycopy(formats, 0, newFormats, beforeWrap, afterWrap);
offsets = newOffsets;
timesUs = newTimesUs;
flags = newFlags;
sizes = newSizes;
encryptionKeys = newEncryptionKeys;
+ formats = newFormats;
relativeReadIndex = 0;
relativeWriteIndex = capacity;
queueSize = capacity;
diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsExtractorWrapper.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsExtractorWrapper.java
index 1099bc860e..cdcca2771f 100644
--- a/library/src/main/java/com/google/android/exoplayer/hls/HlsExtractorWrapper.java
+++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsExtractorWrapper.java
@@ -44,7 +44,6 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
private final SparseArray