Add RepresentationHolder.selectedBaseUrl and use it for new chunks
PiperOrigin-RevId: 384968532
This commit is contained in:
parent
99abb4e1e9
commit
78ecb10ac0
@ -46,6 +46,29 @@ public final class DashUtil {
|
||||
/**
|
||||
* Builds a {@link DataSpec} for a given {@link RangedUri} belonging to {@link Representation}.
|
||||
*
|
||||
* @param baseUrl The base url with which to resolve the request URI.
|
||||
* @param requestUri The {@link RangedUri} of the data to request.
|
||||
* @param cacheKey An optional cache key.
|
||||
* @param flags Flags to be set on the returned {@link DataSpec}. See {@link
|
||||
* DataSpec.Builder#setFlags(int)}.
|
||||
* @return The {@link DataSpec}.
|
||||
*/
|
||||
public static DataSpec buildDataSpec(
|
||||
String baseUrl, RangedUri requestUri, @Nullable String cacheKey, int flags) {
|
||||
return new DataSpec.Builder()
|
||||
.setUri(requestUri.resolveUri(baseUrl))
|
||||
.setPosition(requestUri.start)
|
||||
.setLength(requestUri.length)
|
||||
.setKey(cacheKey)
|
||||
.setFlags(flags)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link DataSpec} for a given {@link RangedUri} belonging to {@link Representation}.
|
||||
*
|
||||
* <p>Uses the first base URL of the representation to build the data spec.
|
||||
*
|
||||
* @param representation The {@link Representation} to which the request belongs.
|
||||
* @param requestUri The {@link RangedUri} of the data to request.
|
||||
* @param flags Flags to be set on the returned {@link DataSpec}. See {@link
|
||||
@ -54,13 +77,8 @@ public final class DashUtil {
|
||||
*/
|
||||
public static DataSpec buildDataSpec(
|
||||
Representation representation, RangedUri requestUri, int flags) {
|
||||
return new DataSpec.Builder()
|
||||
.setUri(requestUri.resolveUri(representation.baseUrls.get(0).url))
|
||||
.setPosition(requestUri.start)
|
||||
.setLength(requestUri.length)
|
||||
.setKey(representation.getCacheKey())
|
||||
.setFlags(flags)
|
||||
.build();
|
||||
return buildDataSpec(
|
||||
representation.baseUrls.get(0).url, requestUri, representation.getCacheKey(), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,6 +114,7 @@ public final class DashUtil {
|
||||
}
|
||||
}
|
||||
Format manifestFormat = representation.format;
|
||||
@Nullable
|
||||
Format sampleFormat = DashUtil.loadSampleFormat(dataSource, primaryTrackType, representation);
|
||||
return sampleFormat == null
|
||||
? manifestFormat
|
||||
@ -109,24 +128,46 @@ public final class DashUtil {
|
||||
* @param trackType The type of the representation. Typically one of the {@link
|
||||
* com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
|
||||
* @param representation The representation which initialization chunk belongs to.
|
||||
* @param baseUrlIndex The index of the base URL to be picked from the {@link
|
||||
* Representation#baseUrls list of base URLs}.
|
||||
* @return the sample {@link Format} of the given representation.
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
*/
|
||||
@Nullable
|
||||
public static Format loadSampleFormat(
|
||||
DataSource dataSource, int trackType, Representation representation) throws IOException {
|
||||
DataSource dataSource, int trackType, Representation representation, int baseUrlIndex)
|
||||
throws IOException {
|
||||
if (representation.getInitializationUri() == null) {
|
||||
return null;
|
||||
}
|
||||
ChunkExtractor chunkExtractor = newChunkExtractor(trackType, representation.format);
|
||||
try {
|
||||
loadInitializationData(chunkExtractor, dataSource, representation, /* loadIndex= */ false);
|
||||
loadInitializationData(
|
||||
chunkExtractor, dataSource, representation, baseUrlIndex, /* loadIndex= */ false);
|
||||
} finally {
|
||||
chunkExtractor.release();
|
||||
}
|
||||
return Assertions.checkStateNotNull(chunkExtractor.getSampleFormats())[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initialization data for the {@code representation} and returns the sample {@link Format}.
|
||||
*
|
||||
* <p>Uses the first base URL for loading the format.
|
||||
*
|
||||
* @param dataSource The source from which the data should be loaded.
|
||||
* @param trackType The type of the representation. Typically one of the {@link
|
||||
* com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
|
||||
* @param representation The representation which initialization chunk belongs to.
|
||||
* @return the sample {@link Format} of the given representation.
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
*/
|
||||
@Nullable
|
||||
public static Format loadSampleFormat(
|
||||
DataSource dataSource, int trackType, Representation representation) throws IOException {
|
||||
return loadSampleFormat(dataSource, trackType, representation, /* baseUrlIndex= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initialization and index data for the {@code representation} and returns the {@link
|
||||
* ChunkIndex}.
|
||||
@ -135,6 +176,38 @@ public final class DashUtil {
|
||||
* @param trackType The type of the representation. Typically one of the {@link
|
||||
* com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
|
||||
* @param representation The representation which initialization chunk belongs to.
|
||||
* @param baseUrlIndex The index of the base URL with which to resolve the request URI.
|
||||
* @return The {@link ChunkIndex} of the given representation, or null if no initialization or
|
||||
* index data exists.
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
*/
|
||||
@Nullable
|
||||
public static ChunkIndex loadChunkIndex(
|
||||
DataSource dataSource, int trackType, Representation representation, int baseUrlIndex)
|
||||
throws IOException {
|
||||
if (representation.getInitializationUri() == null) {
|
||||
return null;
|
||||
}
|
||||
ChunkExtractor chunkExtractor = newChunkExtractor(trackType, representation.format);
|
||||
try {
|
||||
loadInitializationData(
|
||||
chunkExtractor, dataSource, representation, baseUrlIndex, /* loadIndex= */ true);
|
||||
} finally {
|
||||
chunkExtractor.release();
|
||||
}
|
||||
return chunkExtractor.getChunkIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initialization and index data for the {@code representation} and returns the {@link
|
||||
* ChunkIndex}.
|
||||
*
|
||||
* <p>Uses the first base URL for loading the index.
|
||||
*
|
||||
* @param dataSource The source from which the data should be loaded.
|
||||
* @param trackType The type of the representation. Typically one of the {@link
|
||||
* com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
|
||||
* @param representation The representation which initialization chunk belongs to.
|
||||
* @return The {@link ChunkIndex} of the given representation, or null if no initialization or
|
||||
* index data exists.
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
@ -142,16 +215,7 @@ public final class DashUtil {
|
||||
@Nullable
|
||||
public static ChunkIndex loadChunkIndex(
|
||||
DataSource dataSource, int trackType, Representation representation) throws IOException {
|
||||
if (representation.getInitializationUri() == null) {
|
||||
return null;
|
||||
}
|
||||
ChunkExtractor chunkExtractor = newChunkExtractor(trackType, representation.format);
|
||||
try {
|
||||
loadInitializationData(chunkExtractor, dataSource, representation, /* loadIndex= */ true);
|
||||
} finally {
|
||||
chunkExtractor.release();
|
||||
}
|
||||
return chunkExtractor.getChunkIndex();
|
||||
return loadChunkIndex(dataSource, trackType, representation, /* baseUrlIndex= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,6 +225,7 @@ public final class DashUtil {
|
||||
* @param chunkExtractor The {@link ChunkExtractor} to use.
|
||||
* @param dataSource The source from which the data should be loaded.
|
||||
* @param representation The representation which initialization chunk belongs to.
|
||||
* @param baseUrlIndex The index of the base URL with which to resolve the request URI.
|
||||
* @param loadIndex Whether to load index data too.
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
*/
|
||||
@ -168,6 +233,7 @@ public final class DashUtil {
|
||||
ChunkExtractor chunkExtractor,
|
||||
DataSource dataSource,
|
||||
Representation representation,
|
||||
int baseUrlIndex,
|
||||
boolean loadIndex)
|
||||
throws IOException {
|
||||
RangedUri initializationUri = Assertions.checkNotNull(representation.getInitializationUri());
|
||||
@ -179,24 +245,54 @@ public final class DashUtil {
|
||||
}
|
||||
// It's common for initialization and index data to be stored adjacently. Attempt to merge
|
||||
// the two requests together to request both at once.
|
||||
requestUri = initializationUri.attemptMerge(indexUri, representation.baseUrls.get(0).url);
|
||||
requestUri =
|
||||
initializationUri.attemptMerge(indexUri, representation.baseUrls.get(baseUrlIndex).url);
|
||||
if (requestUri == null) {
|
||||
loadInitializationData(dataSource, representation, chunkExtractor, initializationUri);
|
||||
loadInitializationData(
|
||||
dataSource, representation, baseUrlIndex, chunkExtractor, initializationUri);
|
||||
requestUri = indexUri;
|
||||
}
|
||||
} else {
|
||||
requestUri = initializationUri;
|
||||
}
|
||||
loadInitializationData(dataSource, representation, chunkExtractor, requestUri);
|
||||
loadInitializationData(dataSource, representation, baseUrlIndex, chunkExtractor, requestUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initialization data for the {@code representation} and optionally index data then returns
|
||||
* a {@link BundledChunkExtractor} which contains the output.
|
||||
*
|
||||
* <p>Uses the first base URL for loading the initialization data.
|
||||
*
|
||||
* @param chunkExtractor The {@link ChunkExtractor} to use.
|
||||
* @param dataSource The source from which the data should be loaded.
|
||||
* @param representation The representation which initialization chunk belongs to.
|
||||
* @param loadIndex Whether to load index data too.
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
*/
|
||||
public static void loadInitializationData(
|
||||
ChunkExtractor chunkExtractor,
|
||||
DataSource dataSource,
|
||||
Representation representation,
|
||||
boolean loadIndex)
|
||||
throws IOException {
|
||||
loadInitializationData(
|
||||
chunkExtractor, dataSource, representation, /* baseUrlIndex= */ 0, loadIndex);
|
||||
}
|
||||
|
||||
private static void loadInitializationData(
|
||||
DataSource dataSource,
|
||||
Representation representation,
|
||||
int baseUrlIndex,
|
||||
ChunkExtractor chunkExtractor,
|
||||
RangedUri requestUri)
|
||||
throws IOException {
|
||||
DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri, /* flags= */ 0);
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representation.baseUrls.get(baseUrlIndex).url,
|
||||
requestUri,
|
||||
representation.getCacheKey(),
|
||||
/* flags= */ 0);
|
||||
InitializationChunk initializationChunk =
|
||||
new InitializationChunk(
|
||||
dataSource,
|
||||
|
@ -40,6 +40,7 @@ import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
|
||||
import com.google.android.exoplayer2.source.chunk.SingleSampleMediaChunk;
|
||||
import com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerTrackEmsgHandler;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.BaseUrl;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
@ -203,6 +204,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
new RepresentationHolder(
|
||||
periodDurationUs,
|
||||
representation,
|
||||
representation.baseUrls.get(0),
|
||||
BundledChunkExtractor.FACTORY.createProgressiveMediaExtractor(
|
||||
trackType,
|
||||
representation.format,
|
||||
@ -562,14 +564,20 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
if (initializationUri != null) {
|
||||
// It's common for initialization and index data to be stored adjacently. Attempt to merge
|
||||
// the two requests together to request both at once.
|
||||
requestUri = initializationUri.attemptMerge(indexUri, representation.baseUrls.get(0).url);
|
||||
requestUri =
|
||||
initializationUri.attemptMerge(indexUri, representationHolder.selectedBaseUrl.url);
|
||||
if (requestUri == null) {
|
||||
requestUri = initializationUri;
|
||||
}
|
||||
} else {
|
||||
requestUri = indexUri;
|
||||
}
|
||||
DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri, /* flags= */ 0);
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
requestUri,
|
||||
representation.getCacheKey(),
|
||||
/* flags= */ 0);
|
||||
return new InitializationChunk(
|
||||
dataSource,
|
||||
dataSpec,
|
||||
@ -593,7 +601,6 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
Representation representation = representationHolder.representation;
|
||||
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
|
||||
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
|
||||
String baseUrl = representation.baseUrls.get(0).url;
|
||||
if (representationHolder.chunkExtractor == null) {
|
||||
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
|
||||
int flags =
|
||||
@ -601,7 +608,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
firstSegmentNum, nowPeriodTimeUs)
|
||||
? 0
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
segmentUri,
|
||||
representation.getCacheKey(),
|
||||
flags);
|
||||
return new SingleSampleMediaChunk(
|
||||
dataSource,
|
||||
dataSpec,
|
||||
@ -617,7 +629,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
int segmentCount = 1;
|
||||
for (int i = 1; i < maxSegmentCount; i++) {
|
||||
RangedUri nextSegmentUri = representationHolder.getSegmentUrl(firstSegmentNum + i);
|
||||
@Nullable RangedUri mergedSegmentUri = segmentUri.attemptMerge(nextSegmentUri, baseUrl);
|
||||
@Nullable
|
||||
RangedUri mergedSegmentUri =
|
||||
segmentUri.attemptMerge(nextSegmentUri, representationHolder.selectedBaseUrl.url);
|
||||
if (mergedSegmentUri == null) {
|
||||
// Unable to merge segment fetches because the URIs do not merge.
|
||||
break;
|
||||
@ -636,7 +650,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
representationHolder.isSegmentAvailableAtFullNetworkSpeed(segmentNum, nowPeriodTimeUs)
|
||||
? 0
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri, flags);
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
segmentUri,
|
||||
representation.getCacheKey(),
|
||||
flags);
|
||||
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
|
||||
return new ContainerMediaChunk(
|
||||
dataSource,
|
||||
@ -691,7 +710,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
representationHolder.isSegmentAvailableAtFullNetworkSpeed(currentIndex, nowPeriodTimeUs)
|
||||
? 0
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
return DashUtil.buildDataSpec(representationHolder.representation, segmentUri, flags);
|
||||
return DashUtil.buildDataSpec(
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
segmentUri,
|
||||
representationHolder.representation.getCacheKey(),
|
||||
flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -713,6 +736,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
@Nullable /* package */ final ChunkExtractor chunkExtractor;
|
||||
|
||||
public final Representation representation;
|
||||
public final BaseUrl selectedBaseUrl;
|
||||
@Nullable public final DashSegmentIndex segmentIndex;
|
||||
|
||||
private final long periodDurationUs;
|
||||
@ -721,11 +745,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
/* package */ RepresentationHolder(
|
||||
long periodDurationUs,
|
||||
Representation representation,
|
||||
BaseUrl selectedBaseUrl,
|
||||
@Nullable ChunkExtractor chunkExtractor,
|
||||
long segmentNumShift,
|
||||
@Nullable DashSegmentIndex segmentIndex) {
|
||||
this.periodDurationUs = periodDurationUs;
|
||||
this.representation = representation;
|
||||
this.selectedBaseUrl = selectedBaseUrl;
|
||||
this.segmentNumShift = segmentNumShift;
|
||||
this.chunkExtractor = chunkExtractor;
|
||||
this.segmentIndex = segmentIndex;
|
||||
@ -735,26 +761,41 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
/* package */ RepresentationHolder copyWithNewRepresentation(
|
||||
long newPeriodDurationUs, Representation newRepresentation)
|
||||
throws BehindLiveWindowException {
|
||||
DashSegmentIndex oldIndex = representation.getIndex();
|
||||
DashSegmentIndex newIndex = newRepresentation.getIndex();
|
||||
@Nullable DashSegmentIndex oldIndex = representation.getIndex();
|
||||
@Nullable DashSegmentIndex newIndex = newRepresentation.getIndex();
|
||||
|
||||
if (oldIndex == null) {
|
||||
// Segment numbers cannot shift if the index isn't defined by the manifest.
|
||||
return new RepresentationHolder(
|
||||
newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, oldIndex);
|
||||
newPeriodDurationUs,
|
||||
newRepresentation,
|
||||
selectedBaseUrl,
|
||||
chunkExtractor,
|
||||
segmentNumShift,
|
||||
oldIndex);
|
||||
}
|
||||
|
||||
if (!oldIndex.isExplicit()) {
|
||||
// Segment numbers cannot shift if the index isn't explicit.
|
||||
return new RepresentationHolder(
|
||||
newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, newIndex);
|
||||
newPeriodDurationUs,
|
||||
newRepresentation,
|
||||
selectedBaseUrl,
|
||||
chunkExtractor,
|
||||
segmentNumShift,
|
||||
newIndex);
|
||||
}
|
||||
|
||||
long oldIndexSegmentCount = oldIndex.getSegmentCount(newPeriodDurationUs);
|
||||
if (oldIndexSegmentCount == 0) {
|
||||
// Segment numbers cannot shift if the old index was empty.
|
||||
return new RepresentationHolder(
|
||||
newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, newIndex);
|
||||
newPeriodDurationUs,
|
||||
newRepresentation,
|
||||
selectedBaseUrl,
|
||||
chunkExtractor,
|
||||
segmentNumShift,
|
||||
newIndex);
|
||||
}
|
||||
|
||||
long oldIndexFirstSegmentNum = oldIndex.getFirstSegmentNum();
|
||||
@ -786,13 +827,23 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
- newIndexFirstSegmentNum;
|
||||
}
|
||||
return new RepresentationHolder(
|
||||
newPeriodDurationUs, newRepresentation, chunkExtractor, newSegmentNumShift, newIndex);
|
||||
newPeriodDurationUs,
|
||||
newRepresentation,
|
||||
selectedBaseUrl,
|
||||
chunkExtractor,
|
||||
newSegmentNumShift,
|
||||
newIndex);
|
||||
}
|
||||
|
||||
@CheckResult
|
||||
/* package */ RepresentationHolder copyWithNewSegmentIndex(DashSegmentIndex segmentIndex) {
|
||||
return new RepresentationHolder(
|
||||
periodDurationUs, representation, chunkExtractor, segmentNumShift, segmentIndex);
|
||||
periodDurationUs,
|
||||
representation,
|
||||
selectedBaseUrl,
|
||||
chunkExtractor,
|
||||
segmentNumShift,
|
||||
segmentIndex);
|
||||
}
|
||||
|
||||
public long getFirstSegmentNum() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user