Commit b14e0935 by aquilescanta Committed by Oliver Woodman

Add container format sniffing in HLS

Issue:#2025

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=211977802
parent da88b346
......@@ -32,6 +32,8 @@
[VR180](https://github.com/google/spatial-media/blob/master/docs/vr180.md).
* HLS:
* Support PlayReady.
* Add container format sniffing
([#2025](https://github.com/google/ExoPlayer/issues/2025)).
* Support alternative `EXT-X-KEY` tags.
* Support `EXT-X-INDEPENDENT-SEGMENTS` in the master playlist.
* Support variable substitution
......
......@@ -20,7 +20,10 @@ import android.util.Pair;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.util.TimestampAdjuster;
import java.io.IOException;
import java.util.List;
import java.util.Map;
......@@ -45,9 +48,14 @@ public interface HlsExtractorFactory {
* @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.
* @param sniffingExtractorInput The first extractor input that will be passed to the returned
* extractor's {@link Extractor#read(ExtractorInput, PositionHolder)}. Must only be used to
* call {@link Extractor#sniff(ExtractorInput)}.
* @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.
* @throws InterruptedException If the thread is interrupted while sniffing.
* @throws IOException If an I/O error is encountered while sniffing.
*/
Pair<Extractor, Boolean> createExtractor(
Extractor previousExtractor,
......@@ -56,5 +64,7 @@ public interface HlsExtractorFactory {
List<Format> muxedCaptionFormats,
DrmInitData drmInitData,
TimestampAdjuster timestampAdjuster,
Map<String, List<String>> responseHeaders);
Map<String, List<String>> responseHeaders,
ExtractorInput sniffingExtractorInput)
throws InterruptedException, IOException;
}
......@@ -73,15 +73,13 @@ import java.util.concurrent.atomic.AtomicInteger;
private final List<Format> muxedCaptionFormats;
private final DrmInitData drmInitData;
private final Extractor previousExtractor;
private final Id3Decoder id3Decoder;
private final ParsableByteArray id3Data;
private Extractor extractor;
private boolean isPackedAudioExtractor;
private Id3Decoder id3Decoder;
private ParsableByteArray id3Data;
private HlsSampleStreamWrapper output;
private int initSegmentBytesLoaded;
private int nextLoadPosition;
private boolean id3TimestampPeeked;
private boolean initLoadCompleted;
private volatile boolean loadCanceled;
private boolean loadCompleted;
......@@ -158,6 +156,8 @@ import java.util.concurrent.atomic.AtomicInteger;
previousExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber
|| shouldSpliceIn ? null : previousChunk.extractor;
} else {
id3Decoder = new Id3Decoder();
id3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
shouldSpliceIn = false;
}
this.previousExtractor = previousExtractor;
......@@ -244,12 +244,6 @@ import java.util.concurrent.atomic.AtomicInteger;
}
try {
ExtractorInput input = prepareExtraction(dataSource, loadDataSpec);
if (isPackedAudioExtractor && !id3TimestampPeeked) {
long id3Timestamp = peekId3PrivTimestamp(input);
id3TimestampPeeked = true;
output.setSampleOffsetUs(id3Timestamp != C.TIME_UNSET
? timestampAdjuster.adjustTsTimestamp(id3Timestamp) : startTimeUs);
}
if (skipLoadedBytes) {
input.skipFully(nextLoadPosition);
}
......@@ -267,10 +261,16 @@ import java.util.concurrent.atomic.AtomicInteger;
}
private DefaultExtractorInput prepareExtraction(DataSource dataSource, DataSpec dataSpec)
throws IOException {
throws IOException, InterruptedException {
long bytesToRead = dataSource.open(dataSpec);
DefaultExtractorInput extractorInput =
new DefaultExtractorInput(dataSource, dataSpec.absoluteStreamPosition, bytesToRead);
if (extractor == null) {
long id3Timestamp = peekId3PrivTimestamp(extractorInput);
extractorInput.resetPeekPosition();
Pair<Extractor, Boolean> extractorData =
extractorFactory.createExtractor(
previousExtractor,
......@@ -279,22 +279,26 @@ import java.util.concurrent.atomic.AtomicInteger;
muxedCaptionFormats,
drmInitData,
timestampAdjuster,
dataSource.getResponseHeaders());
dataSource.getResponseHeaders(),
extractorInput);
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);
boolean isPackedAudioExtractor = extractorData.second;
if (isPackedAudioExtractor) {
output.setSampleOffsetUs(
id3Timestamp != C.TIME_UNSET
? timestampAdjuster.adjustTsTimestamp(id3Timestamp)
: startTimeUs);
}
initLoadCompleted = reusingExtractor && initDataSpec != null;
output.init(uid, shouldSpliceIn, reusingExtractor);
if (!reusingExtractor) {
extractor.init(output);
}
}
return new DefaultExtractorInput(dataSource, dataSpec.absoluteStreamPosition, bytesToRead);
return extractorInput;
}
/**
......@@ -309,7 +313,8 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
private long peekId3PrivTimestamp(ExtractorInput input) throws IOException, InterruptedException {
input.resetPeekPosition();
if (!input.peekFully(id3Data.data, 0, Id3Decoder.ID3_HEADER_LENGTH, true)) {
if (input.getLength() < Id3Decoder.ID3_HEADER_LENGTH
|| !input.peekFully(id3Data.data, 0, Id3Decoder.ID3_HEADER_LENGTH, true)) {
return C.TIME_UNSET;
}
id3Data.reset(Id3Decoder.ID3_HEADER_LENGTH);
......
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