Skip to content

Commit

Permalink
Use identical cache keys for downloading and playing DASH segments
Browse files Browse the repository at this point in the history
#minor-release #exofixit
Issue: #9370
PiperOrigin-RevId: 395429794
  • Loading branch information
marcbaechinger authored and icbaker committed Sep 8, 2021
1 parent 3cdc8a9 commit d9bc223
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 32 deletions.
7 changes: 5 additions & 2 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand All @@ -77,8 +77,7 @@ public static DataSpec buildDataSpec(
*/
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);
}

/**
Expand Down Expand Up @@ -289,9 +288,9 @@ private static void loadInitializationData(
throws IOException {
DataSpec dataSpec =
DashUtil.buildDataSpec(
representation,
representation.baseUrls.get(baseUrlIndex).url,
requestUri,
representation.getCacheKey(),
/* flags= */ 0);
InitializationChunk initializationChunk =
new InitializationChunk(
Expand All @@ -304,6 +303,21 @@ private static void loadInitializationData(
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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -628,10 +628,7 @@ protected Chunk newInitializationChunk(
}
DataSpec dataSpec =
DashUtil.buildDataSpec(
representationHolder.selectedBaseUrl.url,
requestUri,
representation.getCacheKey(),
/* flags= */ 0);
representation, representationHolder.selectedBaseUrl.url, requestUri, /* flags= */ 0);
return new InitializationChunk(
dataSource,
dataSpec,
Expand Down Expand Up @@ -664,10 +661,7 @@ protected Chunk newMediaChunk(
: 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,
Expand Down Expand Up @@ -706,10 +700,7 @@ protected Chunk newMediaChunk(
: 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,
Expand Down Expand Up @@ -765,9 +756,9 @@ public DataSpec getDataSpec() {
? 0
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
return DashUtil.buildDataSpec(
representationHolder.representation,
representationHolder.selectedBaseUrl.url,
segmentUri,
representationHolder.representation.getCacheKey(),
flags);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -70,6 +73,8 @@
*/
public final class DashDownloader extends SegmentDownloader<DashManifest> {

private final BaseUrlExclusionList baseUrlExclusionList;

/**
* Creates a new instance.
*
Expand Down Expand Up @@ -113,6 +118,7 @@ public DashDownloader(
CacheDataSource.Factory cacheDataSourceFactory,
Executor executor) {
super(mediaItem, manifestParser, cacheDataSourceFactory, executor);
baseUrlExclusionList = new BaseUrlExclusionList();
}

@Override
Expand Down Expand Up @@ -163,28 +169,32 @@ private void addSegmentsForAdaptationSet(
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;
Expand Down Expand Up @@ -67,6 +68,46 @@ public void loadDrmInitDataNoAdaptationSets() throws Exception {
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));
}
Expand Down

0 comments on commit d9bc223

Please sign in to comment.