diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 704671fbdbf..125d8aaf49e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -15,6 +15,7 @@ bandwidth estimates in the future. Always null at the moment. * HLS: * Allow injection of custom playlist trackers. + * Pass HTTP response headers to `HlsExtractorFactory.createExtractor`. * DRM: * Allow DrmInitData to carry a license server URL ([#3393](https://github.com/google/ExoPlayer/issues/3393)). diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java index 702b1126cc3..a0f001ad27b 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/DefaultHlsExtractorFactory.java @@ -31,6 +31,7 @@ import com.google.android.exoplayer2.util.TimestampAdjuster; import java.util.Collections; import java.util.List; +import java.util.Map; /** * Default {@link HlsExtractorFactory} implementation. @@ -48,9 +49,14 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { public static final String WEBVTT_FILE_EXTENSION = ".webvtt"; @Override - public Pair createExtractor(Extractor previousExtractor, Uri uri, - Format format, List muxedCaptionFormats, DrmInitData drmInitData, - TimestampAdjuster timestampAdjuster) { + public Pair createExtractor( + Extractor previousExtractor, + Uri uri, + Format format, + List muxedCaptionFormats, + DrmInitData drmInitData, + TimestampAdjuster timestampAdjuster, + Map> responseHeaders) { String lastPathSegment = uri.getLastPathSegment(); if (lastPathSegment == null) { lastPathSegment = ""; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsExtractorFactory.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsExtractorFactory.java index 3ed6a549db1..a75751815f8 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsExtractorFactory.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsExtractorFactory.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.util.TimestampAdjuster; import java.util.List; +import java.util.Map; /** * Factory for HLS media chunk extractors. @@ -42,12 +43,18 @@ public interface HlsExtractorFactory { * information is available in the master playlist. * @param drmInitData {@link DrmInitData} associated with the chunk. * @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number. + * @param responseHeaders The HTTP response headers associated with the media segment or + * initialization section to extract. * @return A pair containing the {@link Extractor} and a boolean that indicates whether it is a * packed audio extractor. The first element may be {@code previousExtractor} if the factory * has determined it can be re-used. */ - Pair createExtractor(Extractor previousExtractor, Uri uri, Format format, - List muxedCaptionFormats, DrmInitData drmInitData, - TimestampAdjuster timestampAdjuster); - + Pair createExtractor( + Extractor previousExtractor, + Uri uri, + Format format, + List muxedCaptionFormats, + DrmInitData drmInitData, + TimestampAdjuster timestampAdjuster, + Map> responseHeaders); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java index 3cd85565800..2805e35db70 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java @@ -69,12 +69,15 @@ private final boolean hasGapTag; private final TimestampAdjuster timestampAdjuster; private final boolean shouldSpliceIn; - private final Extractor extractor; - private final boolean isPackedAudioExtractor; - private final boolean reusingExtractor; - private final Id3Decoder id3Decoder; - private final ParsableByteArray id3Data; + private final HlsExtractorFactory extractorFactory; + private final List muxedCaptionFormats; + private final DrmInitData drmInitData; + private final Extractor previousExtractor; + private Extractor extractor; + private boolean isPackedAudioExtractor; + private Id3Decoder id3Decoder; + private ParsableByteArray id3Data; private HlsSampleStreamWrapper output; private int initSegmentBytesLoaded; private int bytesLoaded; @@ -145,32 +148,20 @@ public HlsMediaChunk( // Note: this.dataSource and dataSource may be different. this.isEncrypted = this.dataSource instanceof Aes128DataSource; this.hasGapTag = hasGapTag; + this.extractorFactory = extractorFactory; + this.muxedCaptionFormats = muxedCaptionFormats; + this.drmInitData = drmInitData; Extractor previousExtractor = null; if (previousChunk != null) { + id3Decoder = previousChunk.id3Decoder; + id3Data = previousChunk.id3Data; shouldSpliceIn = previousChunk.hlsUrl != hlsUrl; previousExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber || shouldSpliceIn ? null : previousChunk.extractor; } else { shouldSpliceIn = false; } - Pair extractorData = extractorFactory.createExtractor(previousExtractor, - dataSpec.uri, trackFormat, muxedCaptionFormats, drmInitData, timestampAdjuster); - extractor = extractorData.first; - isPackedAudioExtractor = extractorData.second; - reusingExtractor = extractor == previousExtractor; - initLoadCompleted = reusingExtractor && initDataSpec != null; - if (isPackedAudioExtractor) { - if (previousChunk != null && previousChunk.id3Data != null) { - id3Decoder = previousChunk.id3Decoder; - id3Data = previousChunk.id3Data; - } else { - id3Decoder = new Id3Decoder(); - id3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH); - } - } else { - id3Decoder = null; - id3Data = null; - } + this.previousExtractor = previousExtractor; initDataSource = dataSource; uid = uidSource.getAndIncrement(); } @@ -183,10 +174,6 @@ public HlsMediaChunk( */ public void init(HlsSampleStreamWrapper output) { this.output = output; - output.init(uid, shouldSpliceIn, reusingExtractor); - if (!reusingExtractor) { - extractor.init(output); - } } @Override @@ -217,7 +204,7 @@ public void load() throws IOException, InterruptedException { } } - // Internal loading methods. + // Internal methods. private void maybeLoadInitData() throws IOException, InterruptedException { if (initLoadCompleted || initDataSpec == null) { @@ -226,8 +213,7 @@ private void maybeLoadInitData() throws IOException, InterruptedException { } DataSpec initSegmentDataSpec = initDataSpec.subrange(initSegmentBytesLoaded); try { - ExtractorInput input = new DefaultExtractorInput(initDataSource, - initSegmentDataSpec.absoluteStreamPosition, initDataSource.open(initSegmentDataSpec)); + DefaultExtractorInput input = prepareExtraction(initDataSource, initSegmentDataSpec); try { int result = Extractor.RESULT_CONTINUE; while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { @@ -263,8 +249,7 @@ private void loadMedia() throws IOException, InterruptedException { timestampAdjuster.setFirstSampleTimestampUs(startTimeUs); } try { - ExtractorInput input = new DefaultExtractorInput(dataSource, - loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); + ExtractorInput input = prepareExtraction(dataSource, loadDataSpec); if (isPackedAudioExtractor && !id3TimestampPeeked) { long id3Timestamp = peekId3PrivTimestamp(input); id3TimestampPeeked = true; @@ -287,6 +272,37 @@ private void loadMedia() throws IOException, InterruptedException { } } + private DefaultExtractorInput prepareExtraction(DataSource dataSource, DataSpec dataSpec) + throws IOException { + long bytesToRead = dataSource.open(dataSpec); + + if (extractor == null) { + Pair extractorData = + extractorFactory.createExtractor( + previousExtractor, + dataSpec.uri, + trackFormat, + muxedCaptionFormats, + drmInitData, + timestampAdjuster, + dataSource.getResponseHeaders()); + extractor = extractorData.first; + isPackedAudioExtractor = extractorData.second; + boolean reusingExtractor = extractor == previousExtractor; + initLoadCompleted = reusingExtractor && initDataSpec != null; + if (isPackedAudioExtractor && id3Data == null) { + id3Decoder = new Id3Decoder(); + id3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH); + } + output.init(uid, shouldSpliceIn, reusingExtractor); + if (!reusingExtractor) { + extractor.init(output); + } + } + + return new DefaultExtractorInput(dataSource, dataSpec.absoluteStreamPosition, bytesToRead); + } + /** * Peek the presentation timestamp of the first sample in the chunk from an ID3 PRIV as defined * in the HLS spec, version 20, Section 3.4. Returns {@link C#TIME_UNSET} if the frame is not