Reduce the number of parameters required to create an HlsMediaChunk
PiperOrigin-RevId: 233765839
This commit is contained in:
parent
cc153cfca3
commit
04e6061498
@ -290,8 +290,8 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int chunkIndex = (int) (chunkMediaSequence - mediaPlaylist.mediaSequence);
|
int segmentIndexInPlaylist = (int) (chunkMediaSequence - mediaPlaylist.mediaSequence);
|
||||||
if (chunkIndex >= mediaPlaylist.segments.size()) {
|
if (segmentIndexInPlaylist >= mediaPlaylist.segments.size()) {
|
||||||
if (mediaPlaylist.hasEndTag) {
|
if (mediaPlaylist.hasEndTag) {
|
||||||
out.endOfStream = true;
|
out.endOfStream = true;
|
||||||
} else /* Live */ {
|
} else /* Live */ {
|
||||||
@ -306,7 +306,7 @@ import java.util.List;
|
|||||||
expectedPlaylistUrl = null;
|
expectedPlaylistUrl = null;
|
||||||
|
|
||||||
// Handle encryption.
|
// Handle encryption.
|
||||||
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(chunkIndex);
|
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(segmentIndexInPlaylist);
|
||||||
|
|
||||||
// Check if the segment is completely encrypted using the identity key format.
|
// Check if the segment is completely encrypted using the identity key format.
|
||||||
if (segment.fullSegmentEncryptionKeyUri != null) {
|
if (segment.fullSegmentEncryptionKeyUri != null) {
|
||||||
@ -324,44 +324,20 @@ import java.util.List;
|
|||||||
clearEncryptionData();
|
clearEncryptionData();
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSpec initDataSpec = null;
|
|
||||||
Segment initSegment = segment.initializationSegment;
|
|
||||||
if (initSegment != null) {
|
|
||||||
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
|
|
||||||
initDataSpec = new DataSpec(initSegmentUri, initSegment.byterangeOffset,
|
|
||||||
initSegment.byterangeLength, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute start time of the next chunk.
|
|
||||||
long segmentStartTimeInPeriodUs = startOfPlaylistInPeriodUs + segment.relativeStartTimeUs;
|
|
||||||
int discontinuitySequence = mediaPlaylist.discontinuitySequence
|
|
||||||
+ segment.relativeDiscontinuitySequence;
|
|
||||||
TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(
|
|
||||||
discontinuitySequence);
|
|
||||||
|
|
||||||
// Configure the data source and spec for the chunk.
|
|
||||||
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
|
|
||||||
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
|
|
||||||
null);
|
|
||||||
out.chunk =
|
out.chunk =
|
||||||
new HlsMediaChunk(
|
HlsMediaChunk.createInstance(
|
||||||
extractorFactory,
|
extractorFactory,
|
||||||
mediaDataSource,
|
mediaDataSource,
|
||||||
dataSpec,
|
startOfPlaylistInPeriodUs,
|
||||||
initDataSpec,
|
mediaPlaylist,
|
||||||
|
segmentIndexInPlaylist,
|
||||||
selectedUrl,
|
selectedUrl,
|
||||||
muxedCaptionFormats,
|
muxedCaptionFormats,
|
||||||
trackSelection.getSelectionReason(),
|
trackSelection.getSelectionReason(),
|
||||||
trackSelection.getSelectionData(),
|
trackSelection.getSelectionData(),
|
||||||
segmentStartTimeInPeriodUs,
|
|
||||||
segmentStartTimeInPeriodUs + segment.durationUs,
|
|
||||||
chunkMediaSequence,
|
|
||||||
discontinuitySequence,
|
|
||||||
segment.hasGapTag,
|
|
||||||
isTimestampMaster,
|
isTimestampMaster,
|
||||||
timestampAdjuster,
|
timestampAdjusterProvider,
|
||||||
previous,
|
previous,
|
||||||
segment.drmInitData,
|
|
||||||
encryptionKey,
|
encryptionKey,
|
||||||
encryptionIv);
|
encryptionIv);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.hls;
|
package com.google.android.exoplayer2.source.hls;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
@ -27,10 +28,12 @@ import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
|||||||
import com.google.android.exoplayer2.metadata.id3.PrivFrame;
|
import com.google.android.exoplayer2.metadata.id3.PrivFrame;
|
||||||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
||||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
|
||||||
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
||||||
|
import com.google.android.exoplayer2.util.UriUtil;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -42,6 +45,112 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
*/
|
*/
|
||||||
/* package */ final class HlsMediaChunk extends MediaChunk {
|
/* package */ final class HlsMediaChunk extends MediaChunk {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param extractorFactory A {@link HlsExtractorFactory} from which the HLS media chunk extractor
|
||||||
|
* is obtained.
|
||||||
|
* @param dataSource The source from which the data should be loaded.
|
||||||
|
* @param startOfPlaylistInPeriodUs The position of the playlist in the period in microseconds.
|
||||||
|
* @param mediaPlaylist The media playlist from which this chunk was obtained.
|
||||||
|
* @param hlsUrl The url of the playlist from which this chunk was obtained.
|
||||||
|
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
|
||||||
|
* information is available in the master playlist.
|
||||||
|
* @param trackSelectionReason See {@link #trackSelectionReason}.
|
||||||
|
* @param trackSelectionData See {@link #trackSelectionData}.
|
||||||
|
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
|
||||||
|
* @param timestampAdjusterProvider The provider from which to obtain the {@link
|
||||||
|
* TimestampAdjuster}.
|
||||||
|
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
|
||||||
|
* @param fullSegmentEncryptionKey The key to decrypt the full segment, or null if the segment is
|
||||||
|
* not fully encrypted.
|
||||||
|
* @param encryptionIv The AES initialization vector, or null if the segment is not fully
|
||||||
|
* encrypted.
|
||||||
|
*/
|
||||||
|
public static HlsMediaChunk createInstance(
|
||||||
|
HlsExtractorFactory extractorFactory,
|
||||||
|
DataSource dataSource,
|
||||||
|
long startOfPlaylistInPeriodUs,
|
||||||
|
HlsMediaPlaylist mediaPlaylist,
|
||||||
|
int segmentIndexInPlaylist,
|
||||||
|
HlsUrl hlsUrl,
|
||||||
|
List<Format> muxedCaptionFormats,
|
||||||
|
int trackSelectionReason,
|
||||||
|
Object trackSelectionData,
|
||||||
|
boolean isMasterTimestampSource,
|
||||||
|
TimestampAdjusterProvider timestampAdjusterProvider,
|
||||||
|
HlsMediaChunk previousChunk,
|
||||||
|
byte[] fullSegmentEncryptionKey,
|
||||||
|
byte[] encryptionIv) {
|
||||||
|
|
||||||
|
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(segmentIndexInPlaylist);
|
||||||
|
DataSpec dataSpec =
|
||||||
|
new DataSpec(
|
||||||
|
UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url),
|
||||||
|
segment.byterangeOffset,
|
||||||
|
segment.byterangeLength,
|
||||||
|
/* key= */ null);
|
||||||
|
|
||||||
|
DataSpec initDataSpec = null;
|
||||||
|
HlsMediaPlaylist.Segment initSegment = segment.initializationSegment;
|
||||||
|
if (initSegment != null) {
|
||||||
|
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
|
||||||
|
initDataSpec =
|
||||||
|
new DataSpec(
|
||||||
|
initSegmentUri,
|
||||||
|
initSegment.byterangeOffset,
|
||||||
|
initSegment.byterangeLength,
|
||||||
|
/* key= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
long segmentStartTimeInPeriodUs = startOfPlaylistInPeriodUs + segment.relativeStartTimeUs;
|
||||||
|
long segmentEndTimeInPeriodUs = segmentStartTimeInPeriodUs + segment.durationUs;
|
||||||
|
int discontinuitySequenceNumber =
|
||||||
|
mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence;
|
||||||
|
|
||||||
|
Extractor previousExtractor = null;
|
||||||
|
Id3Decoder id3Decoder;
|
||||||
|
ParsableByteArray scratchId3Data;
|
||||||
|
boolean shouldSpliceIn;
|
||||||
|
if (previousChunk != null) {
|
||||||
|
id3Decoder = previousChunk.id3Decoder;
|
||||||
|
scratchId3Data = previousChunk.scratchId3Data;
|
||||||
|
shouldSpliceIn = previousChunk.hlsUrl != hlsUrl || !previousChunk.loadCompleted;
|
||||||
|
previousExtractor =
|
||||||
|
previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber || shouldSpliceIn
|
||||||
|
? null
|
||||||
|
: previousChunk.extractor;
|
||||||
|
} else {
|
||||||
|
id3Decoder = new Id3Decoder();
|
||||||
|
scratchId3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
|
||||||
|
shouldSpliceIn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HlsMediaChunk(
|
||||||
|
extractorFactory,
|
||||||
|
dataSource,
|
||||||
|
dataSpec,
|
||||||
|
initDataSpec,
|
||||||
|
hlsUrl,
|
||||||
|
muxedCaptionFormats,
|
||||||
|
trackSelectionReason,
|
||||||
|
trackSelectionData,
|
||||||
|
segmentStartTimeInPeriodUs,
|
||||||
|
segmentEndTimeInPeriodUs,
|
||||||
|
/* chunkMediaSequence= */ mediaPlaylist.mediaSequence + segmentIndexInPlaylist,
|
||||||
|
discontinuitySequenceNumber,
|
||||||
|
segment.hasGapTag,
|
||||||
|
isMasterTimestampSource,
|
||||||
|
/* timestampAdjuster= */ timestampAdjusterProvider.getAdjuster(discontinuitySequenceNumber),
|
||||||
|
segment.drmInitData,
|
||||||
|
previousExtractor,
|
||||||
|
id3Decoder,
|
||||||
|
scratchId3Data,
|
||||||
|
shouldSpliceIn,
|
||||||
|
fullSegmentEncryptionKey,
|
||||||
|
encryptionIv);
|
||||||
|
}
|
||||||
|
|
||||||
public static final String PRIV_TIMESTAMP_FRAME_OWNER =
|
public static final String PRIV_TIMESTAMP_FRAME_OWNER =
|
||||||
"com.apple.streaming.transportStreamTimestamp";
|
"com.apple.streaming.transportStreamTimestamp";
|
||||||
|
|
||||||
@ -74,7 +183,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
private final DrmInitData drmInitData;
|
private final DrmInitData drmInitData;
|
||||||
private final Extractor previousExtractor;
|
private final Extractor previousExtractor;
|
||||||
private final Id3Decoder id3Decoder;
|
private final Id3Decoder id3Decoder;
|
||||||
private final ParsableByteArray id3Data;
|
private final ParsableByteArray scratchId3Data;
|
||||||
|
|
||||||
private Extractor extractor;
|
private Extractor extractor;
|
||||||
private HlsSampleStreamWrapper output;
|
private HlsSampleStreamWrapper output;
|
||||||
@ -84,32 +193,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
private volatile boolean loadCanceled;
|
private volatile boolean loadCanceled;
|
||||||
private boolean loadCompleted;
|
private boolean loadCompleted;
|
||||||
|
|
||||||
/**
|
private HlsMediaChunk(
|
||||||
* @param extractorFactory A {@link HlsExtractorFactory} from which the HLS media chunk extractor
|
|
||||||
* is obtained.
|
|
||||||
* @param dataSource The source from which the data should be loaded.
|
|
||||||
* @param dataSpec Defines the data to be loaded.
|
|
||||||
* @param initDataSpec Defines the initialization data to be fed to new extractors. May be null.
|
|
||||||
* @param hlsUrl The url of the playlist from which this chunk was obtained.
|
|
||||||
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
|
|
||||||
* information is available in the master playlist.
|
|
||||||
* @param trackSelectionReason See {@link #trackSelectionReason}.
|
|
||||||
* @param trackSelectionData See {@link #trackSelectionData}.
|
|
||||||
* @param startTimeUs The start time of the chunk in microseconds.
|
|
||||||
* @param endTimeUs The end time of the chunk in microseconds.
|
|
||||||
* @param chunkMediaSequence The media sequence number of the chunk.
|
|
||||||
* @param discontinuitySequenceNumber The discontinuity sequence number of the chunk.
|
|
||||||
* @param hasGapTag Whether the chunk is tagged with EXT-X-GAP.
|
|
||||||
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
|
|
||||||
* @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number.
|
|
||||||
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
|
|
||||||
* @param drmInitData A {@link DrmInitData} to sideload to the extractor.
|
|
||||||
* @param fullSegmentEncryptionKey The key to decrypt the full segment, or null if the segment is
|
|
||||||
* not fully encrypted.
|
|
||||||
* @param encryptionIv The AES initialization vector, or null if the segment is not fully
|
|
||||||
* encrypted.
|
|
||||||
*/
|
|
||||||
public HlsMediaChunk(
|
|
||||||
HlsExtractorFactory extractorFactory,
|
HlsExtractorFactory extractorFactory,
|
||||||
DataSource dataSource,
|
DataSource dataSource,
|
||||||
DataSpec dataSpec,
|
DataSpec dataSpec,
|
||||||
@ -125,8 +209,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
boolean hasGapTag,
|
boolean hasGapTag,
|
||||||
boolean isMasterTimestampSource,
|
boolean isMasterTimestampSource,
|
||||||
TimestampAdjuster timestampAdjuster,
|
TimestampAdjuster timestampAdjuster,
|
||||||
HlsMediaChunk previousChunk,
|
|
||||||
DrmInitData drmInitData,
|
DrmInitData drmInitData,
|
||||||
|
Extractor previousExtractor,
|
||||||
|
Id3Decoder id3Decoder,
|
||||||
|
ParsableByteArray scratchId3Data,
|
||||||
|
boolean shouldSpliceIn,
|
||||||
byte[] fullSegmentEncryptionKey,
|
byte[] fullSegmentEncryptionKey,
|
||||||
byte[] encryptionIv) {
|
byte[] encryptionIv) {
|
||||||
super(
|
super(
|
||||||
@ -148,19 +235,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
this.extractorFactory = extractorFactory;
|
this.extractorFactory = extractorFactory;
|
||||||
this.muxedCaptionFormats = muxedCaptionFormats;
|
this.muxedCaptionFormats = muxedCaptionFormats;
|
||||||
this.drmInitData = drmInitData;
|
this.drmInitData = drmInitData;
|
||||||
Extractor previousExtractor = null;
|
|
||||||
if (previousChunk != null) {
|
|
||||||
id3Decoder = previousChunk.id3Decoder;
|
|
||||||
id3Data = previousChunk.id3Data;
|
|
||||||
shouldSpliceIn = previousChunk.hlsUrl != hlsUrl || !previousChunk.loadCompleted;
|
|
||||||
previousExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber
|
|
||||||
|| shouldSpliceIn ? null : previousChunk.extractor;
|
|
||||||
} else {
|
|
||||||
id3Decoder = new Id3Decoder();
|
|
||||||
id3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
|
|
||||||
shouldSpliceIn = false;
|
|
||||||
}
|
|
||||||
this.previousExtractor = previousExtractor;
|
this.previousExtractor = previousExtractor;
|
||||||
|
this.id3Decoder = id3Decoder;
|
||||||
|
this.scratchId3Data = scratchId3Data;
|
||||||
|
this.shouldSpliceIn = shouldSpliceIn;
|
||||||
initDataSource = dataSource;
|
initDataSource = dataSource;
|
||||||
uid = uidSource.getAndIncrement();
|
uid = uidSource.getAndIncrement();
|
||||||
}
|
}
|
||||||
@ -314,26 +392,26 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
private long peekId3PrivTimestamp(ExtractorInput input) throws IOException, InterruptedException {
|
private long peekId3PrivTimestamp(ExtractorInput input) throws IOException, InterruptedException {
|
||||||
input.resetPeekPosition();
|
input.resetPeekPosition();
|
||||||
try {
|
try {
|
||||||
input.peekFully(id3Data.data, 0, Id3Decoder.ID3_HEADER_LENGTH);
|
input.peekFully(scratchId3Data.data, 0, Id3Decoder.ID3_HEADER_LENGTH);
|
||||||
} catch (EOFException e) {
|
} catch (EOFException e) {
|
||||||
// The input isn't long enough for there to be any ID3 data.
|
// The input isn't long enough for there to be any ID3 data.
|
||||||
return C.TIME_UNSET;
|
return C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
id3Data.reset(Id3Decoder.ID3_HEADER_LENGTH);
|
scratchId3Data.reset(Id3Decoder.ID3_HEADER_LENGTH);
|
||||||
int id = id3Data.readUnsignedInt24();
|
int id = scratchId3Data.readUnsignedInt24();
|
||||||
if (id != Id3Decoder.ID3_TAG) {
|
if (id != Id3Decoder.ID3_TAG) {
|
||||||
return C.TIME_UNSET;
|
return C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
id3Data.skipBytes(3); // version(2), flags(1).
|
scratchId3Data.skipBytes(3); // version(2), flags(1).
|
||||||
int id3Size = id3Data.readSynchSafeInt();
|
int id3Size = scratchId3Data.readSynchSafeInt();
|
||||||
int requiredCapacity = id3Size + Id3Decoder.ID3_HEADER_LENGTH;
|
int requiredCapacity = id3Size + Id3Decoder.ID3_HEADER_LENGTH;
|
||||||
if (requiredCapacity > id3Data.capacity()) {
|
if (requiredCapacity > scratchId3Data.capacity()) {
|
||||||
byte[] data = id3Data.data;
|
byte[] data = scratchId3Data.data;
|
||||||
id3Data.reset(requiredCapacity);
|
scratchId3Data.reset(requiredCapacity);
|
||||||
System.arraycopy(data, 0, id3Data.data, 0, Id3Decoder.ID3_HEADER_LENGTH);
|
System.arraycopy(data, 0, scratchId3Data.data, 0, Id3Decoder.ID3_HEADER_LENGTH);
|
||||||
}
|
}
|
||||||
input.peekFully(id3Data.data, Id3Decoder.ID3_HEADER_LENGTH, id3Size);
|
input.peekFully(scratchId3Data.data, Id3Decoder.ID3_HEADER_LENGTH, id3Size);
|
||||||
Metadata metadata = id3Decoder.decode(id3Data.data, id3Size);
|
Metadata metadata = id3Decoder.decode(scratchId3Data.data, id3Size);
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
return C.TIME_UNSET;
|
return C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
@ -343,11 +421,12 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
if (frame instanceof PrivFrame) {
|
if (frame instanceof PrivFrame) {
|
||||||
PrivFrame privFrame = (PrivFrame) frame;
|
PrivFrame privFrame = (PrivFrame) frame;
|
||||||
if (PRIV_TIMESTAMP_FRAME_OWNER.equals(privFrame.owner)) {
|
if (PRIV_TIMESTAMP_FRAME_OWNER.equals(privFrame.owner)) {
|
||||||
System.arraycopy(privFrame.privateData, 0, id3Data.data, 0, 8 /* timestamp size */);
|
System.arraycopy(
|
||||||
id3Data.reset(8);
|
privFrame.privateData, 0, scratchId3Data.data, 0, 8 /* timestamp size */);
|
||||||
|
scratchId3Data.reset(8);
|
||||||
// The top 31 bits should be zeros, but explicitly zero them to wrap in the case that the
|
// The top 31 bits should be zeros, but explicitly zero them to wrap in the case that the
|
||||||
// streaming provider forgot. See: https://github.com/google/ExoPlayer/pull/3495.
|
// streaming provider forgot. See: https://github.com/google/ExoPlayer/pull/3495.
|
||||||
return id3Data.readLong() & 0x1FFFFFFFFL;
|
return scratchId3Data.readLong() & 0x1FFFFFFFFL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user