Use identical cache keys for downloading and playing DASH segments
#minor-release #exofixit Issue: #9370 PiperOrigin-RevId: 395429794
This commit is contained in:
parent
3cdc8a9ea3
commit
d9bc22314a
@ -39,8 +39,11 @@
|
||||
([#9024](https://github.com/google/ExoPlayer/issues/9024)).
|
||||
* Fix accessibility focus in `PlayerControlView`
|
||||
([#9111](https://github.com/google/ExoPlayer/issues/9111)).
|
||||
* Fix issue that `StyledPlayerView` and `PlayerView` don't update UI
|
||||
when available player commands change.
|
||||
* Fix issue that `StyledPlayerView` and `PlayerView` don't update UI when
|
||||
available player commands change.
|
||||
* DASH
|
||||
* Use identical cache keys for downloading and playing DASH segments
|
||||
([#9370](https://github.com/google/ExoPlayer/issues/9370)).
|
||||
* Remove deprecated symbols:
|
||||
* Remove `Renderer.VIDEO_SCALING_MODE_*` constants. Use identically named
|
||||
constants in `C` instead.
|
||||
|
@ -46,20 +46,20 @@ public final class DashUtil {
|
||||
/**
|
||||
* Builds a {@link DataSpec} for a given {@link RangedUri} belonging to {@link Representation}.
|
||||
*
|
||||
* @param representation The {@link Representation} to which the request belongs.
|
||||
* @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) {
|
||||
Representation representation, String baseUrl, RangedUri requestUri, int flags) {
|
||||
return new DataSpec.Builder()
|
||||
.setUri(requestUri.resolveUri(baseUrl))
|
||||
.setPosition(requestUri.start)
|
||||
.setLength(requestUri.length)
|
||||
.setKey(cacheKey)
|
||||
.setKey(resolveCacheKey(representation, requestUri))
|
||||
.setFlags(flags)
|
||||
.build();
|
||||
}
|
||||
@ -77,8 +77,7 @@ public final class DashUtil {
|
||||
*/
|
||||
public static DataSpec buildDataSpec(
|
||||
Representation representation, RangedUri requestUri, int flags) {
|
||||
return buildDataSpec(
|
||||
representation.baseUrls.get(0).url, requestUri, representation.getCacheKey(), flags);
|
||||
return buildDataSpec(representation, representation.baseUrls.get(0).url, requestUri, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -289,9 +288,9 @@ public final class DashUtil {
|
||||
throws IOException {
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representation,
|
||||
representation.baseUrls.get(baseUrlIndex).url,
|
||||
requestUri,
|
||||
representation.getCacheKey(),
|
||||
/* flags= */ 0);
|
||||
InitializationChunk initializationChunk =
|
||||
new InitializationChunk(
|
||||
@ -304,6 +303,21 @@ public final class DashUtil {
|
||||
initializationChunk.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the cache key to be used when requesting the given ranged URI for the given {@link
|
||||
* Representation}.
|
||||
*
|
||||
* @param representation The {@link Representation} to which the URI belongs to.
|
||||
* @param rangedUri The URI for which to resolve the cache key.
|
||||
* @return The cache key.
|
||||
*/
|
||||
public static String resolveCacheKey(Representation representation, RangedUri rangedUri) {
|
||||
@Nullable String cacheKey = representation.getCacheKey();
|
||||
return cacheKey != null
|
||||
? cacheKey
|
||||
: rangedUri.resolveUri(representation.baseUrls.get(0).url).toString();
|
||||
}
|
||||
|
||||
private static ChunkExtractor newChunkExtractor(int trackType, Format format) {
|
||||
String mimeType = format.containerMimeType;
|
||||
boolean isWebm =
|
||||
|
@ -628,10 +628,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
}
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
requestUri,
|
||||
representation.getCacheKey(),
|
||||
/* flags= */ 0);
|
||||
representation, representationHolder.selectedBaseUrl.url, requestUri, /* flags= */ 0);
|
||||
return new InitializationChunk(
|
||||
dataSource,
|
||||
dataSpec,
|
||||
@ -664,10 +661,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
segmentUri,
|
||||
representation.getCacheKey(),
|
||||
flags);
|
||||
representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
|
||||
return new SingleSampleMediaChunk(
|
||||
dataSource,
|
||||
dataSpec,
|
||||
@ -706,10 +700,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
DataSpec dataSpec =
|
||||
DashUtil.buildDataSpec(
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
segmentUri,
|
||||
representation.getCacheKey(),
|
||||
flags);
|
||||
representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
|
||||
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
|
||||
return new ContainerMediaChunk(
|
||||
dataSource,
|
||||
@ -765,9 +756,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
||||
? 0
|
||||
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
|
||||
return DashUtil.buildDataSpec(
|
||||
representationHolder.representation,
|
||||
representationHolder.selectedBaseUrl.url,
|
||||
segmentUri,
|
||||
representationHolder.representation.getCacheKey(),
|
||||
flags);
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,15 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.dash.offline;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer2.offline.DownloadException;
|
||||
import com.google.android.exoplayer2.offline.SegmentDownloader;
|
||||
import com.google.android.exoplayer2.source.dash.BaseUrlExclusionList;
|
||||
import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
|
||||
import com.google.android.exoplayer2.source.dash.DashUtil;
|
||||
import com.google.android.exoplayer2.source.dash.DashWrappingSegmentIndex;
|
||||
@ -70,6 +73,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
*/
|
||||
public final class DashDownloader extends SegmentDownloader<DashManifest> {
|
||||
|
||||
private final BaseUrlExclusionList baseUrlExclusionList;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
@ -113,6 +118,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
|
||||
CacheDataSource.Factory cacheDataSourceFactory,
|
||||
Executor executor) {
|
||||
super(mediaItem, manifestParser, cacheDataSourceFactory, executor);
|
||||
baseUrlExclusionList = new BaseUrlExclusionList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -163,28 +169,32 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
|
||||
throw new DownloadException("Unbounded segment index");
|
||||
}
|
||||
|
||||
String baseUrl = representation.baseUrls.get(0).url;
|
||||
RangedUri initializationUri = representation.getInitializationUri();
|
||||
String baseUrl = castNonNull(baseUrlExclusionList.selectBaseUrl(representation.baseUrls)).url;
|
||||
@Nullable RangedUri initializationUri = representation.getInitializationUri();
|
||||
if (initializationUri != null) {
|
||||
addSegment(periodStartUs, baseUrl, initializationUri, out);
|
||||
out.add(createSegment(representation, baseUrl, periodStartUs, initializationUri));
|
||||
}
|
||||
RangedUri indexUri = representation.getIndexUri();
|
||||
@Nullable RangedUri indexUri = representation.getIndexUri();
|
||||
if (indexUri != null) {
|
||||
addSegment(periodStartUs, baseUrl, indexUri, out);
|
||||
out.add(createSegment(representation, baseUrl, periodStartUs, indexUri));
|
||||
}
|
||||
long firstSegmentNum = index.getFirstSegmentNum();
|
||||
long lastSegmentNum = firstSegmentNum + segmentCount - 1;
|
||||
for (long j = firstSegmentNum; j <= lastSegmentNum; j++) {
|
||||
addSegment(periodStartUs + index.getTimeUs(j), baseUrl, index.getSegmentUrl(j), out);
|
||||
out.add(
|
||||
createSegment(
|
||||
representation,
|
||||
baseUrl,
|
||||
periodStartUs + index.getTimeUs(j),
|
||||
index.getSegmentUrl(j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSegment(
|
||||
long startTimeUs, String baseUrl, RangedUri rangedUri, ArrayList<Segment> out) {
|
||||
DataSpec dataSpec =
|
||||
new DataSpec(rangedUri.resolveUri(baseUrl), rangedUri.start, rangedUri.length);
|
||||
out.add(new Segment(startTimeUs, dataSpec));
|
||||
private Segment createSegment(
|
||||
Representation representation, String baseUrl, long startTimeUs, RangedUri rangedUri) {
|
||||
DataSpec dataSpec = DashUtil.buildDataSpec(representation, baseUrl, rangedUri, /* flags= */ 0);
|
||||
return new Segment(startTimeUs, dataSpec);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
||||
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.Period;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
||||
import com.google.android.exoplayer2.upstream.DummyDataSource;
|
||||
@ -67,6 +68,46 @@ public final class DashUtilTest {
|
||||
assertThat(format).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveCacheKey_representationCacheKeyIsNull_resolvesRangedUriWithFirstBaseUrl() {
|
||||
ImmutableList<BaseUrl> baseUrls =
|
||||
ImmutableList.of(new BaseUrl("http://www.google.com"), new BaseUrl("http://www.foo.com"));
|
||||
Representation.SingleSegmentRepresentation representation =
|
||||
new Representation.SingleSegmentRepresentation(
|
||||
/* revisionId= */ 1L,
|
||||
new Format.Builder().build(),
|
||||
baseUrls,
|
||||
new SingleSegmentBase(),
|
||||
/* inbandEventStreams= */ null,
|
||||
/* cacheKey= */ null,
|
||||
/* contentLength= */ 1);
|
||||
RangedUri rangedUri = new RangedUri("path/to/resource", /* start= */ 0, /* length= */ 1);
|
||||
|
||||
String cacheKey = DashUtil.resolveCacheKey(representation, rangedUri);
|
||||
|
||||
assertThat(cacheKey).isEqualTo("http://www.google.com/path/to/resource");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveCacheKey_representationCacheKeyDefined_usesRepresentationCacheKey() {
|
||||
ImmutableList<BaseUrl> baseUrls =
|
||||
ImmutableList.of(new BaseUrl("http://www.google.com"), new BaseUrl("http://www.foo.com"));
|
||||
Representation.SingleSegmentRepresentation representation =
|
||||
new Representation.SingleSegmentRepresentation(
|
||||
/* revisionId= */ 1L,
|
||||
new Format.Builder().build(),
|
||||
baseUrls,
|
||||
new SingleSegmentBase(),
|
||||
/* inbandEventStreams= */ null,
|
||||
"cacheKey",
|
||||
/* contentLength= */ 1);
|
||||
RangedUri rangedUri = new RangedUri("path/to/resource", /* start= */ 0, /* length= */ 1);
|
||||
|
||||
String cacheKey = DashUtil.resolveCacheKey(representation, rangedUri);
|
||||
|
||||
assertThat(cacheKey).isEqualTo("cacheKey");
|
||||
}
|
||||
|
||||
private static Period newPeriod(AdaptationSet... adaptationSets) {
|
||||
return new Period("", 0, Arrays.asList(adaptationSets));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user