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)).
|
([#9024](https://github.com/google/ExoPlayer/issues/9024)).
|
||||||
* Fix accessibility focus in `PlayerControlView`
|
* Fix accessibility focus in `PlayerControlView`
|
||||||
([#9111](https://github.com/google/ExoPlayer/issues/9111)).
|
([#9111](https://github.com/google/ExoPlayer/issues/9111)).
|
||||||
* Fix issue that `StyledPlayerView` and `PlayerView` don't update UI
|
* Fix issue that `StyledPlayerView` and `PlayerView` don't update UI when
|
||||||
when available player commands change.
|
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 deprecated symbols:
|
||||||
* Remove `Renderer.VIDEO_SCALING_MODE_*` constants. Use identically named
|
* Remove `Renderer.VIDEO_SCALING_MODE_*` constants. Use identically named
|
||||||
constants in `C` instead.
|
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}.
|
* 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 {
|
|||||||
*/
|
*/
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
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 {
|
|||||||
: 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 {
|
|||||||
: 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 {
|
|||||||
? 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 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
|||||||
*/
|
*/
|
||||||
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> {
|
|||||||
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> {
|
|||||||
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;
|
|||||||
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;
|
||||||
@ -67,6 +68,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));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user