Commit 69c75fb5 by bachinger Committed by Christos Tsilopoulos

Use identical cache keys for downloading and playing DASH segments

Issue: #9370
PiperOrigin-RevId: 395429794
parent ee2ef1c3
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
thrown from `Requirements.isInternetConnectivityValidated` on devices thrown from `Requirements.isInternetConnectivityValidated` on devices
running Android 11 running Android 11
([#9002](https://github.com/google/ExoPlayer/issues/9002)). ([#9002](https://github.com/google/ExoPlayer/issues/9002)).
* DASH:
* Use identical cache keys for downloading and playing DASH segments
([#9370](https://github.com/google/ExoPlayer/issues/9370)).
* RTSP: * RTSP:
* Handle when additional spaces are in SDP's RTPMAP atrribute * Handle when additional spaces are in SDP's RTPMAP atrribute
([#9379](https://github.com/google/ExoPlayer/issues/9379)). ([#9379](https://github.com/google/ExoPlayer/issues/9379)).
......
...@@ -46,20 +46,20 @@ public final class DashUtil { ...@@ -46,20 +46,20 @@ public final class DashUtil {
/** /**
* Builds a {@link DataSpec} for a given {@link RangedUri} belonging to {@link Representation}. * 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 baseUrl The base url with which to resolve the request URI.
* @param requestUri The {@link RangedUri} of the data to request. * @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 * @param flags Flags to be set on the returned {@link DataSpec}. See {@link
* DataSpec.Builder#setFlags(int)}. * DataSpec.Builder#setFlags(int)}.
* @return The {@link DataSpec}. * @return The {@link DataSpec}.
*/ */
public static DataSpec buildDataSpec( 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() return new DataSpec.Builder()
.setUri(requestUri.resolveUri(baseUrl)) .setUri(requestUri.resolveUri(baseUrl))
.setPosition(requestUri.start) .setPosition(requestUri.start)
.setLength(requestUri.length) .setLength(requestUri.length)
.setKey(cacheKey) .setKey(resolveCacheKey(representation, requestUri))
.setFlags(flags) .setFlags(flags)
.build(); .build();
} }
...@@ -77,8 +77,7 @@ public final class DashUtil { ...@@ -77,8 +77,7 @@ public final class DashUtil {
*/ */
public static DataSpec buildDataSpec( public static DataSpec buildDataSpec(
Representation representation, RangedUri requestUri, int flags) { Representation representation, RangedUri requestUri, int flags) {
return buildDataSpec( return buildDataSpec(representation, representation.baseUrls.get(0).url, requestUri, flags);
representation.baseUrls.get(0).url, requestUri, representation.getCacheKey(), flags);
} }
/** /**
...@@ -289,9 +288,9 @@ public final class DashUtil { ...@@ -289,9 +288,9 @@ public final class DashUtil {
throws IOException { throws IOException {
DataSpec dataSpec = DataSpec dataSpec =
DashUtil.buildDataSpec( DashUtil.buildDataSpec(
representation,
representation.baseUrls.get(baseUrlIndex).url, representation.baseUrls.get(baseUrlIndex).url,
requestUri, requestUri,
representation.getCacheKey(),
/* flags= */ 0); /* flags= */ 0);
InitializationChunk initializationChunk = InitializationChunk initializationChunk =
new InitializationChunk( new InitializationChunk(
...@@ -304,6 +303,21 @@ public final class DashUtil { ...@@ -304,6 +303,21 @@ public final class DashUtil {
initializationChunk.load(); 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) { private static ChunkExtractor newChunkExtractor(int trackType, Format format) {
String mimeType = format.containerMimeType; String mimeType = format.containerMimeType;
boolean isWebm = boolean isWebm =
......
...@@ -628,10 +628,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -628,10 +628,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
DataSpec dataSpec = DataSpec dataSpec =
DashUtil.buildDataSpec( DashUtil.buildDataSpec(
representationHolder.selectedBaseUrl.url, representation, representationHolder.selectedBaseUrl.url, requestUri, /* flags= */ 0);
requestUri,
representation.getCacheKey(),
/* flags= */ 0);
return new InitializationChunk( return new InitializationChunk(
dataSource, dataSource,
dataSpec, dataSpec,
...@@ -664,10 +661,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -664,10 +661,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED; : DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec = DataSpec dataSpec =
DashUtil.buildDataSpec( DashUtil.buildDataSpec(
representationHolder.selectedBaseUrl.url, representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
segmentUri,
representation.getCacheKey(),
flags);
return new SingleSampleMediaChunk( return new SingleSampleMediaChunk(
dataSource, dataSource,
dataSpec, dataSpec,
...@@ -706,10 +700,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -706,10 +700,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED; : DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
DataSpec dataSpec = DataSpec dataSpec =
DashUtil.buildDataSpec( DashUtil.buildDataSpec(
representationHolder.selectedBaseUrl.url, representation, representationHolder.selectedBaseUrl.url, segmentUri, flags);
segmentUri,
representation.getCacheKey(),
flags);
long sampleOffsetUs = -representation.presentationTimeOffsetUs; long sampleOffsetUs = -representation.presentationTimeOffsetUs;
return new ContainerMediaChunk( return new ContainerMediaChunk(
dataSource, dataSource,
...@@ -765,9 +756,9 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -765,9 +756,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
? 0 ? 0
: DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED; : DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED;
return DashUtil.buildDataSpec( return DashUtil.buildDataSpec(
representationHolder.representation,
representationHolder.selectedBaseUrl.url, representationHolder.selectedBaseUrl.url,
segmentUri, segmentUri,
representationHolder.representation.getCacheKey(),
flags); flags);
} }
......
...@@ -15,12 +15,15 @@ ...@@ -15,12 +15,15 @@
*/ */
package com.google.android.exoplayer2.source.dash.offline; package com.google.android.exoplayer2.source.dash.offline;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.extractor.ChunkIndex; import com.google.android.exoplayer2.extractor.ChunkIndex;
import com.google.android.exoplayer2.offline.DownloadException; import com.google.android.exoplayer2.offline.DownloadException;
import com.google.android.exoplayer2.offline.SegmentDownloader; 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.DashSegmentIndex;
import com.google.android.exoplayer2.source.dash.DashUtil; import com.google.android.exoplayer2.source.dash.DashUtil;
import com.google.android.exoplayer2.source.dash.DashWrappingSegmentIndex; import com.google.android.exoplayer2.source.dash.DashWrappingSegmentIndex;
...@@ -70,6 +73,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -70,6 +73,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
*/ */
public final class DashDownloader extends SegmentDownloader<DashManifest> { public final class DashDownloader extends SegmentDownloader<DashManifest> {
private final BaseUrlExclusionList baseUrlExclusionList;
/** /**
* Creates a new instance. * Creates a new instance.
* *
...@@ -113,6 +118,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> { ...@@ -113,6 +118,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
CacheDataSource.Factory cacheDataSourceFactory, CacheDataSource.Factory cacheDataSourceFactory,
Executor executor) { Executor executor) {
super(mediaItem, manifestParser, cacheDataSourceFactory, executor); super(mediaItem, manifestParser, cacheDataSourceFactory, executor);
baseUrlExclusionList = new BaseUrlExclusionList();
} }
@Override @Override
...@@ -163,28 +169,32 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> { ...@@ -163,28 +169,32 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
throw new DownloadException("Unbounded segment index"); throw new DownloadException("Unbounded segment index");
} }
String baseUrl = representation.baseUrls.get(0).url; String baseUrl = castNonNull(baseUrlExclusionList.selectBaseUrl(representation.baseUrls)).url;
RangedUri initializationUri = representation.getInitializationUri(); @Nullable RangedUri initializationUri = representation.getInitializationUri();
if (initializationUri != null) { 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) { if (indexUri != null) {
addSegment(periodStartUs, baseUrl, indexUri, out); out.add(createSegment(representation, baseUrl, periodStartUs, indexUri));
} }
long firstSegmentNum = index.getFirstSegmentNum(); long firstSegmentNum = index.getFirstSegmentNum();
long lastSegmentNum = firstSegmentNum + segmentCount - 1; long lastSegmentNum = firstSegmentNum + segmentCount - 1;
for (long j = firstSegmentNum; j <= lastSegmentNum; j++) { 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( private Segment createSegment(
long startTimeUs, String baseUrl, RangedUri rangedUri, ArrayList<Segment> out) { Representation representation, String baseUrl, long startTimeUs, RangedUri rangedUri) {
DataSpec dataSpec = DataSpec dataSpec = DashUtil.buildDataSpec(representation, baseUrl, rangedUri, /* flags= */ 0);
new DataSpec(rangedUri.resolveUri(baseUrl), rangedUri.start, rangedUri.length); return new Segment(startTimeUs, dataSpec);
out.add(new Segment(startTimeUs, dataSpec));
} }
@Nullable @Nullable
......
...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; ...@@ -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.AdaptationSet;
import com.google.android.exoplayer2.source.dash.manifest.BaseUrl; 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.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.Representation;
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase; import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
import com.google.android.exoplayer2.upstream.DummyDataSource; import com.google.android.exoplayer2.upstream.DummyDataSource;
...@@ -69,6 +70,46 @@ public final class DashUtilTest { ...@@ -69,6 +70,46 @@ public final class DashUtilTest {
assertThat(format).isNull(); 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) { private static Period newPeriod(AdaptationSet... adaptationSets) {
return new Period("", 0, Arrays.asList(adaptationSets)); return new Period("", 0, Arrays.asList(adaptationSets));
} }
......
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