Commit 61014503 by aquilescanta Committed by Oliver Woodman

Merge initialization chunk into HlsMediaChunk

This will allow creating the extractor in the HlsMediaChunk.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=139915713
parent 9ac7f64c
......@@ -106,7 +106,6 @@ import java.util.Locale;
private byte[] scratchSpace;
private IOException fatalError;
private HlsInitializationChunk lastLoadedInitializationChunk;
private Uri encryptionKeyUri;
private byte[] encryptionKey;
private String encryptionIvString;
......@@ -266,20 +265,17 @@ import java.util.Locale;
clearEncryptionData();
}
// Compute start and end times, and the sequence number of the next chunk.
// Compute start time and sequence number of the next chunk.
long startTimeUs = segment.startTimeUs;
if (previous != null && !switchingVariant) {
startTimeUs = previous.getAdjustedEndTimeUs();
}
long endTimeUs = startTimeUs + segment.durationUs;
Format format = variants[newVariantIndex].format;
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
// Set the extractor that will read the chunk.
Extractor extractor;
boolean useInitializedExtractor = lastLoadedInitializationChunk != null
&& lastLoadedInitializationChunk.format == format;
boolean needNewExtractor = previous == null
|| previous.discontinuitySequenceNumber != segment.discontinuitySequenceNumber
|| format != previous.trackFormat;
......@@ -305,64 +301,56 @@ import java.util.Locale;
} else if (lastPathSegment.endsWith(MP4_FILE_EXTENSION)) {
isTimestampMaster = true;
if (needNewExtractor) {
if (useInitializedExtractor) {
extractor = lastLoadedInitializationChunk.extractor;
} else {
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
segment.discontinuitySequenceNumber, startTimeUs);
extractor = new FragmentedMp4Extractor(0, timestampAdjuster);
}
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
segment.discontinuitySequenceNumber, startTimeUs);
extractor = new FragmentedMp4Extractor(0, timestampAdjuster);
} else {
extractorNeedsInit = false;
extractor = previous.extractor;
}
} else if (needNewExtractor) {
// MPEG-2 TS segments, but we need a new extractor.
isTimestampMaster = true;
if (useInitializedExtractor) {
extractor = lastLoadedInitializationChunk.extractor;
} else {
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
segment.discontinuitySequenceNumber, startTimeUs);
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultTsPayloadReaderFactory.Flags
int esReaderFactoryFlags = 0;
String codecs = format.codecs;
if (!TextUtils.isEmpty(codecs)) {
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
// exist. If we know from the codec attribute that they don't exist, then we can
// explicitly ignore them even if they're declared.
if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM;
}
if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM;
}
timestampAdjuster = timestampAdjusterProvider.getAdjuster(
segment.discontinuitySequenceNumber, startTimeUs);
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultTsPayloadReaderFactory.Flags
int esReaderFactoryFlags = 0;
String codecs = format.codecs;
if (!TextUtils.isEmpty(codecs)) {
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
// exist. If we know from the codec attribute that they don't exist, then we can
// explicitly ignore them even if they're declared.
if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM;
}
if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM;
}
extractor = new TsExtractor(timestampAdjuster,
new DefaultTsPayloadReaderFactory(esReaderFactoryFlags), true);
}
extractor = new TsExtractor(timestampAdjuster,
new DefaultTsPayloadReaderFactory(esReaderFactoryFlags), true);
} else {
// MPEG-2 TS segments, and we need to continue using the same extractor.
extractor = previous.extractor;
extractorNeedsInit = false;
}
// Initialize the extractor.
if (needNewExtractor && mediaPlaylist.initializationSegment != null
&& !useInitializedExtractor) {
out.chunk = buildInitializationChunk(mediaPlaylist, extractor, format);
return;
DataSpec initDataSpec = null;
Segment initSegment = mediaPlaylist.initializationSegment;
if (initSegment != null) {
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
initDataSpec = new DataSpec(initSegmentUri, initSegment.byterangeOffset,
initSegment.byterangeLength, null);
}
lastLoadedInitializationChunk = null;
// Configure the data source and spec for the chunk.
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
null);
out.chunk = new HlsMediaChunk(dataSource, dataSpec, variants[newVariantIndex],
out.chunk = new HlsMediaChunk(dataSource, dataSpec, initDataSpec, variants[newVariantIndex],
trackSelection.getSelectionReason(), trackSelection.getSelectionData(),
startTimeUs, endTimeUs, chunkMediaSequence, segment.discontinuitySequenceNumber,
isTimestampMaster, timestampAdjuster, extractor, extractorNeedsInit, switchingVariant,
encryptionKey, encryptionIv);
segment, chunkMediaSequence, isTimestampMaster, timestampAdjuster, extractor,
extractorNeedsInit, switchingVariant, encryptionKey, encryptionIv);
}
/**
......@@ -376,9 +364,6 @@ import java.util.Locale;
HlsMediaChunk mediaChunk = (HlsMediaChunk) chunk;
playlistTracker.onChunkLoaded(mediaChunk.hlsUrl, mediaChunk.chunkIndex,
mediaChunk.getAdjustedStartTimeUs());
}
if (chunk instanceof HlsInitializationChunk) {
lastLoadedInitializationChunk = (HlsInitializationChunk) chunk;
} else if (chunk instanceof EncryptionKeyChunk) {
EncryptionKeyChunk encryptionKeyChunk = (EncryptionKeyChunk) chunk;
scratchSpace = encryptionKeyChunk.getDataHolder();
......@@ -419,18 +404,6 @@ import java.util.Locale;
// Private methods.
private HlsInitializationChunk buildInitializationChunk(HlsMediaPlaylist mediaPlaylist,
Extractor extractor, Format format) {
Segment initSegment = mediaPlaylist.initializationSegment;
// The initialization segment is required before the actual media chunk.
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
DataSpec initDataSpec = new DataSpec(initSegmentUri, initSegment.byterangeOffset,
initSegment.byterangeLength, null);
return new HlsInitializationChunk(dataSource, initDataSpec,
trackSelection.getSelectionReason(), trackSelection.getSelectionData(), extractor,
format);
}
private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv, int variantIndex,
int trackSelectionReason, Object trackSelectionData) {
DataSpec dataSpec = new DataSpec(keyUri, 0, C.LENGTH_UNSET, null, DataSpec.FLAG_ALLOW_GZIP);
......
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.source.hls;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
/**
* An HLS initialization chunk. Provides the extractor with information required for extracting the
* samples.
*/
/* package */ final class HlsInitializationChunk extends Chunk {
public final Format format;
public final Extractor extractor;
private int bytesLoaded;
private volatile boolean loadCanceled;
public HlsInitializationChunk(DataSource dataSource, DataSpec dataSpec, int trackSelectionReason,
Object trackSelectionData, Extractor extractor, Format format) {
super(dataSource, dataSpec, C.TRACK_TYPE_DEFAULT, null, trackSelectionReason,
trackSelectionData, C.TIME_UNSET, C.TIME_UNSET);
this.extractor = extractor;
this.format = format;
}
/**
* Sets the {@link HlsSampleStreamWrapper} that will receive the sample format information from
* the initialization chunk.
*
* @param output The output that will receive the format information.
*/
public void init(HlsSampleStreamWrapper output) {
extractor.init(output);
}
@Override
public long bytesLoaded() {
return bytesLoaded;
}
@Override
public void cancelLoad() {
loadCanceled = true;
}
@Override
public boolean isLoadCanceled() {
return loadCanceled;
}
@Override
public void load() throws IOException, InterruptedException {
DataSpec loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded);
try {
ExtractorInput input = new DefaultExtractorInput(dataSource,
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
try {
int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, null);
}
} finally {
bytesLoaded = (int) (input.getPosition() - dataSpec.absoluteStreamPosition);
}
} finally {
dataSource.close();
}
}
}
......@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
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.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Util;
......@@ -54,13 +55,17 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public final HlsUrl hlsUrl;
private final DataSource initDataSource;
private final DataSpec initDataSpec;
private final boolean isEncrypted;
private final boolean extractorNeedsInit;
private final boolean shouldSpliceIn;
private final boolean isMasterTimestampSource;
private final TimestampAdjuster timestampAdjuster;
private int initSegmentBytesLoaded;
private int bytesLoaded;
private boolean initLoadCompleted;
private HlsSampleStreamWrapper extractorOutput;
private long adjustedEndTimeUs;
private volatile boolean loadCanceled;
......@@ -69,13 +74,12 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* @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 trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @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 segment The {@link Segment} for which this media chunk is created.
* @param chunkIndex The media sequence number of the chunk.
* @param discontinuitySequenceNumber The discontinuity sequence number of the chunk.
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
* @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number.
* @param extractor The extractor to decode samples from the data.
......@@ -86,23 +90,26 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param encryptionKey For AES encryption chunks, the encryption key.
* @param encryptionIv For AES encryption chunks, the encryption initialization vector.
*/
public HlsMediaChunk(DataSource dataSource, DataSpec dataSpec, HlsUrl hlsUrl,
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs,
int chunkIndex, int discontinuitySequenceNumber, boolean isMasterTimestampSource,
TimestampAdjuster timestampAdjuster, Extractor extractor, boolean extractorNeedsInit,
boolean shouldSpliceIn, byte[] encryptionKey, byte[] encryptionIv) {
public HlsMediaChunk(DataSource dataSource, DataSpec dataSpec, DataSpec initDataSpec,
HlsUrl hlsUrl, int trackSelectionReason, Object trackSelectionData, Segment segment,
int chunkIndex, boolean isMasterTimestampSource, TimestampAdjuster timestampAdjuster,
Extractor extractor, boolean extractorNeedsInit, boolean shouldSpliceIn, byte[] encryptionKey,
byte[] encryptionIv) {
super(buildDataSource(dataSource, encryptionKey, encryptionIv), dataSpec, hlsUrl.format,
trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, chunkIndex);
trackSelectionReason, trackSelectionData, segment.startTimeUs,
segment.startTimeUs + segment.durationUs, chunkIndex);
this.initDataSpec = initDataSpec;
this.hlsUrl = hlsUrl;
this.discontinuitySequenceNumber = discontinuitySequenceNumber;
this.isMasterTimestampSource = isMasterTimestampSource;
this.timestampAdjuster = timestampAdjuster;
this.extractor = extractor;
this.extractorNeedsInit = extractorNeedsInit;
this.shouldSpliceIn = shouldSpliceIn;
// Note: this.dataSource and dataSource may be different.
adjustedEndTimeUs = endTimeUs;
this.isEncrypted = this.dataSource instanceof Aes128DataSource;
initDataSource = dataSource;
discontinuitySequenceNumber = segment.discontinuitySequenceNumber;
adjustedEndTimeUs = endTimeUs;
uid = UID_SOURCE.getAndIncrement();
}
......@@ -158,6 +165,37 @@ import java.util.concurrent.atomic.AtomicInteger;
@Override
public void load() throws IOException, InterruptedException {
maybeLoadInitData();
if (!loadCanceled) {
loadMedia();
}
}
// Private methods.
private void maybeLoadInitData() throws IOException, InterruptedException {
if (!extractorNeedsInit || initLoadCompleted || initDataSpec == null) {
return;
}
DataSpec initSegmentDataSpec = Util.getRemainderDataSpec(initDataSpec, initSegmentBytesLoaded);
try {
ExtractorInput input = new DefaultExtractorInput(initDataSource,
initSegmentDataSpec.absoluteStreamPosition, initDataSource.open(initSegmentDataSpec));
try {
int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, null);
}
} finally {
initSegmentBytesLoaded += (int) (input.getPosition() - dataSpec.absoluteStreamPosition);
}
} finally {
Util.closeQuietly(dataSource);
}
initLoadCompleted = true;
}
private void loadMedia() throws IOException, InterruptedException {
// If we previously fed part of this chunk to the extractor, we need to skip it this time. For
// encrypted content we need to skip the data by reading it through the source, so as to ensure
// correct decryption of the remainder of the chunk. For clear content, we can request the
......@@ -193,13 +231,11 @@ import java.util.concurrent.atomic.AtomicInteger;
bytesLoaded = (int) (input.getPosition() - dataSpec.absoluteStreamPosition);
}
} finally {
dataSource.close();
Util.closeQuietly(dataSource);
}
loadCompleted = true;
}
// Private methods
/**
* If the content is encrypted, returns an {@link Aes128DataSource} that wraps the original in
* order to decrypt the loaded data. Else returns the original.
......
......@@ -358,8 +358,6 @@ import java.util.LinkedList;
HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable;
mediaChunk.init(this);
mediaChunks.add(mediaChunk);
} else if (loadable instanceof HlsInitializationChunk) {
((HlsInitializationChunk) loadable).init(this);
}
long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount);
eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, trackType, loadable.trackFormat,
......
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