mirror of
https://github.com/samsonjs/media.git
synced 2026-03-31 10:25:48 +00:00
Clean up requesting non-media segments in downloader implementations
- Enable GZIP for media playlist + encryption key chunk requests in HLS, as we do during playback - Pass around DataSpecs rather than Uris. This will be needed for if we add manifest cacheKey support (which seems like a good idea for completeness, if nothing else) PiperOrigin-RevId: 224057139
This commit is contained in:
parent
f8b85739b1
commit
22a8aa311b
5 changed files with 81 additions and 47 deletions
|
|
@ -62,7 +62,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M>> impleme
|
|||
|
||||
private static final int BUFFER_SIZE_BYTES = 128 * 1024;
|
||||
|
||||
private final Uri manifestUri;
|
||||
private final DataSpec manifestDataSpec;
|
||||
private final Cache cache;
|
||||
private final CacheDataSource dataSource;
|
||||
private final CacheDataSource offlineDataSource;
|
||||
|
|
@ -84,7 +84,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M>> impleme
|
|||
*/
|
||||
public SegmentDownloader(
|
||||
Uri manifestUri, List<StreamKey> streamKeys, DownloaderConstructorHelper constructorHelper) {
|
||||
this.manifestUri = manifestUri;
|
||||
this.manifestDataSpec = getCompressibleDataSpec(manifestUri);
|
||||
this.streamKeys = new ArrayList<>(streamKeys);
|
||||
this.cache = constructorHelper.getCache();
|
||||
this.dataSource = constructorHelper.createCacheDataSource();
|
||||
|
|
@ -171,7 +171,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M>> impleme
|
|||
@Override
|
||||
public final void remove() throws InterruptedException {
|
||||
try {
|
||||
M manifest = getManifest(offlineDataSource, manifestUri);
|
||||
M manifest = getManifest(offlineDataSource, manifestDataSpec);
|
||||
List<Segment> segments = getSegments(offlineDataSource, manifest, true);
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
removeDataSpec(segments.get(i).dataSpec);
|
||||
|
|
@ -180,7 +180,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M>> impleme
|
|||
// Ignore exceptions when removing.
|
||||
} finally {
|
||||
// Always attempt to remove the manifest.
|
||||
removeDataSpec(new DataSpec(manifestUri));
|
||||
removeDataSpec(manifestDataSpec);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,11 +190,11 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M>> impleme
|
|||
* Loads and parses the manifest.
|
||||
*
|
||||
* @param dataSource The {@link DataSource} through which to load.
|
||||
* @param uri The manifest uri.
|
||||
* @param dataSpec The manifest {@link DataSpec}.
|
||||
* @return The manifest.
|
||||
* @throws IOException If an error occurs reading data.
|
||||
*/
|
||||
protected abstract M getManifest(DataSource dataSource, Uri uri) throws IOException;
|
||||
protected abstract M getManifest(DataSource dataSource, DataSpec dataSpec) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a list of all downloadable {@link Segment}s for a given manifest.
|
||||
|
|
@ -217,7 +217,7 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M>> impleme
|
|||
// Writes to downloadedSegments and downloadedBytes are safe. See the comment on download().
|
||||
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||
private List<Segment> initDownload() throws IOException, InterruptedException {
|
||||
M manifest = getManifest(dataSource, manifestUri);
|
||||
M manifest = getManifest(dataSource, manifestDataSpec);
|
||||
if (!streamKeys.isEmpty()) {
|
||||
manifest = manifest.copy(streamKeys);
|
||||
}
|
||||
|
|
@ -252,4 +252,12 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M>> impleme
|
|||
CacheUtil.remove(dataSpec, cache, cacheKeyFactory);
|
||||
}
|
||||
|
||||
protected static DataSpec getCompressibleDataSpec(Uri uri) {
|
||||
return new DataSpec(
|
||||
uri,
|
||||
/* absoluteStreamPosition= */ 0,
|
||||
/* length= */ C.LENGTH_UNSET,
|
||||
/* key= */ null,
|
||||
/* flags= */ DataSpec.FLAG_ALLOW_GZIP);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,24 @@ public final class ParsingLoadable<T> implements Loadable {
|
|||
return Assertions.checkNotNull(loadable.getResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a single parsable object.
|
||||
*
|
||||
* @param dataSource The {@link DataSource} through which the object should be read.
|
||||
* @param parser The {@link Parser} to parse the object from the response.
|
||||
* @param dataSpec The {@link DataSpec} of the object to read.
|
||||
* @param type The type of the data. One of the {@link C}{@code DATA_TYPE_*} constants.
|
||||
* @return The parsed object
|
||||
* @throws IOException Thrown if there is an error while loading or parsing.
|
||||
*/
|
||||
public static <T> T load(
|
||||
DataSource dataSource, Parser<? extends T> parser, DataSpec dataSpec, int type)
|
||||
throws IOException {
|
||||
ParsingLoadable<T> loadable = new ParsingLoadable<>(dataSource, dataSpec, type, parser);
|
||||
loadable.load();
|
||||
return Assertions.checkNotNull(loadable.getResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link DataSpec} that defines the data to be loaded.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -28,11 +28,13 @@ import com.google.android.exoplayer2.source.dash.DashUtil;
|
|||
import com.google.android.exoplayer2.source.dash.DashWrappingSegmentIndex;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
|
||||
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.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -73,8 +75,9 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected DashManifest getManifest(DataSource dataSource, Uri uri) throws IOException {
|
||||
return DashUtil.loadManifest(dataSource, uri);
|
||||
protected DashManifest getManifest(DataSource dataSource, DataSpec dataSpec) throws IOException {
|
||||
return ParsingLoadable.load(
|
||||
dataSource, new DashManifestParser(), dataSpec, C.DATA_TYPE_MANIFEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -121,8 +124,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
|
|||
if (!allowIncompleteList) {
|
||||
throw e;
|
||||
}
|
||||
// Loading failed, but generating an incomplete segment list is allowed. Advance to the next
|
||||
// representation.
|
||||
// Generating an incomplete segment list is allowed. Advance to the next representation.
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,35 +71,37 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected HlsPlaylist getManifest(DataSource dataSource, Uri uri) throws IOException {
|
||||
return loadManifest(dataSource, uri);
|
||||
protected HlsPlaylist getManifest(DataSource dataSource, DataSpec dataSpec) throws IOException {
|
||||
return loadManifest(dataSource, dataSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Segment> getSegments(
|
||||
DataSource dataSource, HlsPlaylist playlist, boolean allowIncompleteList) throws IOException {
|
||||
ArrayList<Uri> mediaPlaylistUris = new ArrayList<>();
|
||||
String baseUri = playlist.baseUri;
|
||||
|
||||
ArrayList<DataSpec> mediaPlaylistDataSpecs = new ArrayList<>();
|
||||
if (playlist instanceof HlsMasterPlaylist) {
|
||||
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
|
||||
addResolvedUris(masterPlaylist.baseUri, masterPlaylist.variants, mediaPlaylistUris);
|
||||
addResolvedUris(masterPlaylist.baseUri, masterPlaylist.audios, mediaPlaylistUris);
|
||||
addResolvedUris(masterPlaylist.baseUri, masterPlaylist.subtitles, mediaPlaylistUris);
|
||||
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.variants, mediaPlaylistDataSpecs);
|
||||
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.audios, mediaPlaylistDataSpecs);
|
||||
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.subtitles, mediaPlaylistDataSpecs);
|
||||
} else {
|
||||
mediaPlaylistUris.add(Uri.parse(playlist.baseUri));
|
||||
mediaPlaylistDataSpecs.add(SegmentDownloader.getCompressibleDataSpec(Uri.parse(baseUri)));
|
||||
}
|
||||
ArrayList<Segment> segments = new ArrayList<>();
|
||||
|
||||
ArrayList<Segment> segments = new ArrayList<>();
|
||||
HashSet<Uri> seenEncryptionKeyUris = new HashSet<>();
|
||||
for (Uri mediaPlaylistUri : mediaPlaylistUris) {
|
||||
for (DataSpec mediaPlaylistDataSpec : mediaPlaylistDataSpecs) {
|
||||
segments.add(new Segment(/* startTimeUs= */ 0, mediaPlaylistDataSpec));
|
||||
HlsMediaPlaylist mediaPlaylist;
|
||||
try {
|
||||
mediaPlaylist = (HlsMediaPlaylist) loadManifest(dataSource, mediaPlaylistUri);
|
||||
segments.add(new Segment(mediaPlaylist.startTimeUs, new DataSpec(mediaPlaylistUri)));
|
||||
mediaPlaylist = (HlsMediaPlaylist) loadManifest(dataSource, mediaPlaylistDataSpec);
|
||||
} catch (IOException e) {
|
||||
if (!allowIncompleteList) {
|
||||
throw e;
|
||||
}
|
||||
segments.add(new Segment(0, new DataSpec(mediaPlaylistUri)));
|
||||
// Generating an incomplete segment list is allowed. Advance to the next media playlist.
|
||||
continue;
|
||||
}
|
||||
HlsMediaPlaylist.Segment lastInitSegment = null;
|
||||
|
|
@ -109,39 +111,43 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
|
|||
HlsMediaPlaylist.Segment initSegment = segment.initializationSegment;
|
||||
if (initSegment != null && initSegment != lastInitSegment) {
|
||||
lastInitSegment = initSegment;
|
||||
addSegment(segments, mediaPlaylist, initSegment, seenEncryptionKeyUris);
|
||||
addSegment(mediaPlaylist, initSegment, seenEncryptionKeyUris, segments);
|
||||
}
|
||||
addSegment(segments, mediaPlaylist, segment, seenEncryptionKeyUris);
|
||||
addSegment(mediaPlaylist, segment, seenEncryptionKeyUris, segments);
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
private static HlsPlaylist loadManifest(DataSource dataSource, Uri uri) throws IOException {
|
||||
return ParsingLoadable.load(dataSource, new HlsPlaylistParser(), uri, C.DATA_TYPE_MANIFEST);
|
||||
private void addMediaPlaylistDataSpecs(String baseUri, List<HlsUrl> urls, List<DataSpec> out) {
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
Uri playlistUri = UriUtil.resolveToUri(baseUri, urls.get(i).url);
|
||||
out.add(SegmentDownloader.getCompressibleDataSpec(playlistUri));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSegment(
|
||||
ArrayList<Segment> segments,
|
||||
private static HlsPlaylist loadManifest(DataSource dataSource, DataSpec dataSpec)
|
||||
throws IOException {
|
||||
return ParsingLoadable.load(
|
||||
dataSource, new HlsPlaylistParser(), dataSpec, C.DATA_TYPE_MANIFEST);
|
||||
}
|
||||
|
||||
private void addSegment(
|
||||
HlsMediaPlaylist mediaPlaylist,
|
||||
HlsMediaPlaylist.Segment hlsSegment,
|
||||
HashSet<Uri> seenEncryptionKeyUris) {
|
||||
long startTimeUs = mediaPlaylist.startTimeUs + hlsSegment.relativeStartTimeUs;
|
||||
if (hlsSegment.fullSegmentEncryptionKeyUri != null) {
|
||||
Uri keyUri = UriUtil.resolveToUri(mediaPlaylist.baseUri,
|
||||
hlsSegment.fullSegmentEncryptionKeyUri);
|
||||
HlsMediaPlaylist.Segment segment,
|
||||
HashSet<Uri> seenEncryptionKeyUris,
|
||||
ArrayList<Segment> out) {
|
||||
String baseUri = mediaPlaylist.baseUri;
|
||||
long startTimeUs = mediaPlaylist.startTimeUs + segment.relativeStartTimeUs;
|
||||
if (segment.fullSegmentEncryptionKeyUri != null) {
|
||||
Uri keyUri = UriUtil.resolveToUri(baseUri, segment.fullSegmentEncryptionKeyUri);
|
||||
if (seenEncryptionKeyUris.add(keyUri)) {
|
||||
segments.add(new Segment(startTimeUs, new DataSpec(keyUri)));
|
||||
out.add(new Segment(startTimeUs, SegmentDownloader.getCompressibleDataSpec(keyUri)));
|
||||
}
|
||||
}
|
||||
Uri resolvedUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, hlsSegment.url);
|
||||
segments.add(new Segment(startTimeUs,
|
||||
new DataSpec(resolvedUri, hlsSegment.byterangeOffset, hlsSegment.byterangeLength, null)));
|
||||
}
|
||||
|
||||
private static void addResolvedUris(String baseUri, List<HlsUrl> urls, List<Uri> out) {
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
out.add(UriUtil.resolveToUri(baseUri, urls.get(i).url));
|
||||
}
|
||||
Uri segmentUri = UriUtil.resolveToUri(baseUri, segment.url);
|
||||
DataSpec dataSpec =
|
||||
new DataSpec(segmentUri, segment.byterangeOffset, segment.byterangeLength, /* key= */ null);
|
||||
out.add(new Segment(startTimeUs, dataSpec));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ public final class SsDownloader extends SegmentDownloader<SsManifest> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected SsManifest getManifest(DataSource dataSource, Uri uri) throws IOException {
|
||||
return ParsingLoadable.load(dataSource, new SsManifestParser(), uri, C.DATA_TYPE_MANIFEST);
|
||||
protected SsManifest getManifest(DataSource dataSource, DataSpec dataSpec) throws IOException {
|
||||
return ParsingLoadable.load(dataSource, new SsManifestParser(), dataSpec, C.DATA_TYPE_MANIFEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in a new issue