mirror of
https://github.com/samsonjs/media.git
synced 2026-04-03 10:55:48 +00:00
Move download helpers into core library
Also convert them to exposing periods and track groups, like regular MediaSources do. This gets us much closer to being able to use standard track selection components during offline initialization. The helper is responsible for reverse mapping selected tracks onto physical streams when generating the download action. This is trivial except for the HLS case, which is a TODO for now. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=195500826
This commit is contained in:
parent
4ee1daef0e
commit
416d6c9eeb
15 changed files with 642 additions and 272 deletions
|
|
@ -27,36 +27,24 @@ import android.widget.ArrayAdapter;
|
|||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.offline.DownloadAction;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.offline.ProgressiveDownloadAction;
|
||||
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.Representation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
|
||||
import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction;
|
||||
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.RenditionKey;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.TrackKey;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction;
|
||||
import com.google.android.exoplayer2.offline.ProgressiveDownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.SegmentDownloadAction;
|
||||
import com.google.android.exoplayer2.offline.TrackKey;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.dash.offline.DashDownloadHelper;
|
||||
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadHelper;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadHelper;
|
||||
import com.google.android.exoplayer2.ui.DefaultTrackNameProvider;
|
||||
import com.google.android.exoplayer2.ui.TrackNameProvider;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
import com.google.android.exoplayer2.util.ParcelableArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** An activity for downloading media. */
|
||||
|
|
@ -69,9 +57,10 @@ public class DownloadActivity extends Activity {
|
|||
private String sampleName;
|
||||
|
||||
private TrackNameProvider trackNameProvider;
|
||||
private DownloadHelper<? extends Parcelable> downloadHelper;
|
||||
private ListView representationList;
|
||||
private DownloadHelper downloadHelper;
|
||||
private ListView trackList;
|
||||
private ArrayAdapter<String> arrayAdapter;
|
||||
private ArrayList<TrackKey> trackKeys;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
|
@ -86,9 +75,10 @@ public class DownloadActivity extends Activity {
|
|||
getActionBar().setTitle(sampleName);
|
||||
|
||||
arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_multiple_choice);
|
||||
representationList = findViewById(R.id.representation_list);
|
||||
representationList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
representationList.setAdapter(arrayAdapter);
|
||||
trackList = findViewById(R.id.representation_list);
|
||||
trackList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
trackList.setAdapter(arrayAdapter);
|
||||
trackKeys = new ArrayList<>();
|
||||
|
||||
DemoApplication application = (DemoApplication) getApplication();
|
||||
DataSource.Factory manifestDataSourceFactory =
|
||||
|
|
@ -112,29 +102,37 @@ public class DownloadActivity extends Activity {
|
|||
throw new IllegalStateException("Unsupported type: " + type);
|
||||
}
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
downloadHelper.init();
|
||||
runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onInitialized();
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onInitializationError();
|
||||
}
|
||||
});
|
||||
downloadHelper.prepare(
|
||||
new DownloadHelper.Callback() {
|
||||
@Override
|
||||
public void onPrepared(DownloadHelper helper) {
|
||||
DownloadActivity.this.onPrepared();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareError(DownloadHelper helper, IOException e) {
|
||||
DownloadActivity.this.onPrepareError();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onPrepared() {
|
||||
for (int i = 0; i < downloadHelper.getPeriodCount(); i++) {
|
||||
TrackGroupArray trackGroups = downloadHelper.getTrackGroups(i);
|
||||
for (int j = 0; j < trackGroups.length; j++) {
|
||||
TrackGroup trackGroup = trackGroups.get(j);
|
||||
for (int k = 0; k < trackGroup.length; k++) {
|
||||
arrayAdapter.add(trackNameProvider.getTrackName(trackGroup.getFormat(k)));
|
||||
trackKeys.add(new TrackKey(i, j, k));
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void onPrepareError() {
|
||||
Toast.makeText(
|
||||
getApplicationContext(), R.string.download_manifest_load_error, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
|
||||
// This method is referenced in the layout file
|
||||
|
|
@ -150,26 +148,13 @@ public class DownloadActivity extends Activity {
|
|||
}
|
||||
}
|
||||
|
||||
private void onInitialized() {
|
||||
for (int i = 0; i < downloadHelper.getTrackCount(); i++) {
|
||||
arrayAdapter.add(trackNameProvider.getTrackName(downloadHelper.getTrackFormat(i)));
|
||||
}
|
||||
}
|
||||
|
||||
private void onInitializationError() {
|
||||
Toast.makeText(
|
||||
getApplicationContext(), R.string.download_manifest_load_error, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void startDownload() {
|
||||
int[] selectedTrackIndices = getSelectedTrackIndices();
|
||||
if (selectedTrackIndices.length > 0) {
|
||||
List<TrackKey> selectedTrackKeys = getSelectedTrackKeys();
|
||||
if (trackKeys.isEmpty() || !selectedTrackKeys.isEmpty()) {
|
||||
DownloadService.addDownloadAction(
|
||||
this,
|
||||
DemoDownloadService.class,
|
||||
downloadHelper.getDownloadAction(
|
||||
/* isRemoveAction= */ false, sampleName, selectedTrackIndices));
|
||||
downloadHelper.getDownloadAction(Util.getUtf8Bytes(sampleName), selectedTrackKeys));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,189 +162,35 @@ public class DownloadActivity extends Activity {
|
|||
DownloadService.addDownloadAction(
|
||||
this,
|
||||
DemoDownloadService.class,
|
||||
downloadHelper.getDownloadAction(/* isRemoveAction= */ true, sampleName));
|
||||
for (int i = 0; i < representationList.getChildCount(); i++) {
|
||||
representationList.setItemChecked(i, false);
|
||||
downloadHelper.getRemoveAction(Util.getUtf8Bytes(sampleName)));
|
||||
for (int i = 0; i < trackList.getChildCount(); i++) {
|
||||
trackList.setItemChecked(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void playDownload() {
|
||||
int[] selectedTrackIndices = getSelectedTrackIndices();
|
||||
List<? extends Parcelable> keys = downloadHelper.getTrackKeys(selectedTrackIndices);
|
||||
DownloadAction action = downloadHelper.getDownloadAction(null, getSelectedTrackKeys());
|
||||
List<? extends ParcelableArray> keys = null;
|
||||
if (action instanceof SegmentDownloadAction) {
|
||||
keys = ((SegmentDownloadAction) action).keys;
|
||||
}
|
||||
if (keys.isEmpty()) {
|
||||
playerIntent.removeExtra(PlayerActivity.MANIFEST_FILTER_EXTRA);
|
||||
} else {
|
||||
Parcelable[] keysArray = keys.toArray(new Parcelable[selectedTrackIndices.length]);
|
||||
playerIntent.putExtra(PlayerActivity.MANIFEST_FILTER_EXTRA, new ParcelableArray<>(keysArray));
|
||||
playerIntent.putExtra(
|
||||
PlayerActivity.MANIFEST_FILTER_EXTRA,
|
||||
new ParcelableArray(keys.toArray(new Parcelable[0])));
|
||||
}
|
||||
startActivity(playerIntent);
|
||||
}
|
||||
|
||||
private int[] getSelectedTrackIndices() {
|
||||
ArrayList<Integer> checkedIndices = new ArrayList<>();
|
||||
for (int i = 0; i < representationList.getChildCount(); i++) {
|
||||
if (representationList.isItemChecked(i)) {
|
||||
checkedIndices.add(i);
|
||||
private List<TrackKey> getSelectedTrackKeys() {
|
||||
ArrayList<TrackKey> selectedTrackKeys = new ArrayList<>();
|
||||
for (int i = 0; i < trackList.getChildCount(); i++) {
|
||||
if (trackList.isItemChecked(i)) {
|
||||
selectedTrackKeys.add(trackKeys.get(i));
|
||||
}
|
||||
}
|
||||
return Util.toArray(checkedIndices);
|
||||
}
|
||||
|
||||
private abstract static class DownloadHelper<K extends Parcelable> {
|
||||
|
||||
protected static final Format DUMMY_FORMAT =
|
||||
Format.createContainerFormat(null, null, null, null, Format.NO_VALUE, 0, null);
|
||||
|
||||
protected final Uri uri;
|
||||
protected final DataSource.Factory dataSourceFactory;
|
||||
protected final List<Format> trackFormats;
|
||||
protected final List<K> trackKeys;
|
||||
|
||||
public DownloadHelper(Uri uri, DataSource.Factory dataSourceFactory) {
|
||||
this.uri = uri;
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
trackFormats = new ArrayList<>();
|
||||
trackKeys = new ArrayList<>();
|
||||
}
|
||||
|
||||
public abstract void init() throws IOException;
|
||||
|
||||
public int getTrackCount() {
|
||||
return trackFormats.size();
|
||||
}
|
||||
|
||||
public Format getTrackFormat(int trackIndex) {
|
||||
return trackFormats.get(trackIndex);
|
||||
}
|
||||
|
||||
public List<K> getTrackKeys(int... trackIndices) {
|
||||
if (trackFormats.size() == 1 && trackFormats.get(0) == DUMMY_FORMAT) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<K> keys = new ArrayList<>(trackIndices.length);
|
||||
for (int trackIndex : trackIndices) {
|
||||
keys.add(trackKeys.get(trackIndex));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
public abstract DownloadAction getDownloadAction(
|
||||
boolean isRemoveAction, String sampleName, int... trackIndices);
|
||||
}
|
||||
|
||||
private static final class DashDownloadHelper extends DownloadHelper<RepresentationKey> {
|
||||
|
||||
public DashDownloadHelper(Uri uri, DataSource.Factory dataSourceFactory) {
|
||||
super(uri, dataSourceFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
DataSource dataSource = dataSourceFactory.createDataSource();
|
||||
DashManifest manifest = ParsingLoadable.load(dataSource, new DashManifestParser(), uri);
|
||||
|
||||
for (int periodIndex = 0; periodIndex < manifest.getPeriodCount(); periodIndex++) {
|
||||
List<AdaptationSet> adaptationSets = manifest.getPeriod(periodIndex).adaptationSets;
|
||||
for (int adaptationIndex = 0; adaptationIndex < adaptationSets.size(); adaptationIndex++) {
|
||||
List<Representation> representations =
|
||||
adaptationSets.get(adaptationIndex).representations;
|
||||
int representationsCount = representations.size();
|
||||
for (int i = 0; i < representationsCount; i++) {
|
||||
trackFormats.add(representations.get(i).format);
|
||||
trackKeys.add(new RepresentationKey(periodIndex, adaptationIndex, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
boolean isRemoveAction, String sampleName, int... trackIndices) {
|
||||
return new DashDownloadAction(
|
||||
uri, isRemoveAction, Util.getUtf8Bytes(sampleName), getTrackKeys(trackIndices));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class HlsDownloadHelper extends DownloadHelper<RenditionKey> {
|
||||
|
||||
public HlsDownloadHelper(Uri uri, DataSource.Factory dataSourceFactory) {
|
||||
super(uri, dataSourceFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
DataSource dataSource = dataSourceFactory.createDataSource();
|
||||
HlsPlaylist<?> playlist = ParsingLoadable.load(dataSource, new HlsPlaylistParser(), uri);
|
||||
|
||||
if (playlist instanceof HlsMediaPlaylist) {
|
||||
trackFormats.add(DUMMY_FORMAT);
|
||||
} else {
|
||||
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
|
||||
addRepresentationItems(masterPlaylist.variants, RenditionKey.TYPE_VARIANT);
|
||||
addRepresentationItems(masterPlaylist.audios, RenditionKey.TYPE_AUDIO);
|
||||
addRepresentationItems(masterPlaylist.subtitles, RenditionKey.TYPE_SUBTITLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRepresentationItems(List<HlsUrl> renditions, int renditionGroup) {
|
||||
for (int i = 0; i < renditions.size(); i++) {
|
||||
trackFormats.add(renditions.get(i).format);
|
||||
trackKeys.add(new RenditionKey(renditionGroup, i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
boolean isRemoveAction, String sampleName, int... trackIndices) {
|
||||
return new HlsDownloadAction(
|
||||
uri, isRemoveAction, Util.getUtf8Bytes(sampleName), getTrackKeys(trackIndices));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SsDownloadHelper extends DownloadHelper<TrackKey> {
|
||||
|
||||
public SsDownloadHelper(Uri uri, DataSource.Factory dataSourceFactory) {
|
||||
super(uri, dataSourceFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
DataSource dataSource = dataSourceFactory.createDataSource();
|
||||
SsManifest manifest = ParsingLoadable.load(dataSource, new SsManifestParser(), uri);
|
||||
|
||||
for (int i = 0; i < manifest.streamElements.length; i++) {
|
||||
SsManifest.StreamElement streamElement = manifest.streamElements[i];
|
||||
for (int j = 0; j < streamElement.formats.length; j++) {
|
||||
trackFormats.add(streamElement.formats[j]);
|
||||
trackKeys.add(new TrackKey(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
boolean isRemoveAction, String sampleName, int... trackIndices) {
|
||||
return new SsDownloadAction(
|
||||
uri, isRemoveAction, Util.getUtf8Bytes(sampleName), getTrackKeys(trackIndices));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProgressiveDownloadHelper extends DownloadHelper<Parcelable> {
|
||||
|
||||
public ProgressiveDownloadHelper(Uri uri) {
|
||||
super(uri, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
trackFormats.add(DUMMY_FORMAT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
boolean isRemoveAction, String sampleName, int... trackIndices) {
|
||||
return new ProgressiveDownloadAction(
|
||||
uri, isRemoveAction, Util.getUtf8Bytes(sampleName), /* customCacheKey= */ null);
|
||||
}
|
||||
return selectedTrackKeys;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ import com.google.android.exoplayer2.source.hls.playlist.RenditionKey;
|
|||
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.TrackKey;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.StreamKey;
|
||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
|
||||
|
|
@ -467,7 +467,7 @@ public class PlayerActivity extends Activity
|
|||
buildDataSourceFactory(false))
|
||||
.setManifestParser(
|
||||
new FilteringManifestParser<>(
|
||||
new SsManifestParser(), (List<TrackKey>) manifestFilter))
|
||||
new SsManifestParser(), (List<StreamKey>) manifestFilter))
|
||||
.createMediaSource(uri);
|
||||
case C.TYPE_HLS:
|
||||
return new HlsMediaSource.Factory(mediaDataSourceFactory)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** A helper for initializing and removing downloads. */
|
||||
public abstract class DownloadHelper {
|
||||
|
||||
/** A callback to be notified when the {@link DownloadHelper} is prepared. */
|
||||
public interface Callback {
|
||||
|
||||
/**
|
||||
* Called when preparation completes.
|
||||
*
|
||||
* @param helper The reporting {@link DownloadHelper}.
|
||||
*/
|
||||
void onPrepared(DownloadHelper helper);
|
||||
|
||||
/**
|
||||
* Called when preparation fails.
|
||||
*
|
||||
* @param helper The reporting {@link DownloadHelper}.
|
||||
* @param e The error.
|
||||
*/
|
||||
void onPrepareError(DownloadHelper helper, IOException e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the helper for starting a download.
|
||||
*
|
||||
* @param callback A callback to be notified when preparation completes or fails. The callback
|
||||
* will be invoked on the calling thread unless that thread does not have an associated {@link
|
||||
* Looper}, in which case it will be called on the application's main thread.
|
||||
*/
|
||||
public void prepare(final Callback callback) {
|
||||
final Handler handler =
|
||||
new Handler(Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper());
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
prepareInternal();
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onPrepared(DownloadHelper.this);
|
||||
}
|
||||
});
|
||||
} catch (final IOException e) {
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onPrepareError(DownloadHelper.this, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on a background thread during preparation.
|
||||
*
|
||||
* @throws IOException If preparation fails.
|
||||
*/
|
||||
protected abstract void prepareInternal() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the number of periods for which media is available. Must not be called until after
|
||||
* preparation completes.
|
||||
*/
|
||||
public abstract int getPeriodCount();
|
||||
|
||||
/**
|
||||
* Returns the track groups for the given period. Must not be called until after preparation
|
||||
* completes.
|
||||
*
|
||||
* @param periodIndex The period index.
|
||||
* @return The track groups for the period. May be {@link TrackGroupArray.EMPTY} for single stream
|
||||
* content.
|
||||
*/
|
||||
public abstract TrackGroupArray getTrackGroups(int periodIndex);
|
||||
|
||||
/**
|
||||
* Builds a {@link DownloadAction} for downloading the specified tracks. Must not be called until
|
||||
* after preparation completes.
|
||||
*
|
||||
* @param data Application provided data to store in {@link DownloadAction#data}.
|
||||
* @param trackKeys The selected tracks. If empty, all streams will be downloaded.
|
||||
* @return The built {@link DownloadAction}.
|
||||
*/
|
||||
public abstract DownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys);
|
||||
|
||||
/**
|
||||
* Builds a {@link DownloadAction} for removing the media. May be called in any state.
|
||||
*
|
||||
* @param data Application provided data to store in {@link DownloadAction#data}.
|
||||
* @return The built {@link DownloadAction}.
|
||||
*/
|
||||
public abstract DownloadAction getRemoveAction(@Nullable byte[] data);
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import java.util.List;
|
||||
|
||||
/** A {@link DownloadHelper} for progressive streams. */
|
||||
public final class ProgressiveDownloadHelper extends DownloadHelper {
|
||||
|
||||
private final Uri uri;
|
||||
private final @Nullable String customCacheKey;
|
||||
|
||||
public ProgressiveDownloadHelper(Uri uri) {
|
||||
this(uri, null);
|
||||
}
|
||||
|
||||
public ProgressiveDownloadHelper(Uri uri, @Nullable String customCacheKey) {
|
||||
this.uri = uri;
|
||||
this.customCacheKey = customCacheKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareInternal() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeriodCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackGroupArray getTrackGroups(int periodIndex) {
|
||||
return TrackGroupArray.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
|
||||
return new ProgressiveDownloadAction(uri, false, data, customCacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getRemoveAction(@Nullable byte[] data) {
|
||||
return new ProgressiveDownloadAction(uri, true, data, customCacheKey);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
/**
|
||||
* Identifies a given track by the index of the containing period, the index of the containing group
|
||||
* within the period, and the index of the track within the group.
|
||||
*/
|
||||
public class TrackKey {
|
||||
|
||||
public final int periodIndex;
|
||||
public final int groupIndex;
|
||||
public final int trackIndex;
|
||||
|
||||
/**
|
||||
* @param periodIndex The period index.
|
||||
* @param groupIndex The group index.
|
||||
* @param trackIndex The track index.
|
||||
*/
|
||||
public TrackKey(int periodIndex, int groupIndex, int trackIndex) {
|
||||
this.periodIndex = periodIndex;
|
||||
this.groupIndex = groupIndex;
|
||||
this.trackIndex = trackIndex;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.dash.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.TrackKey;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
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.Representation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** A {@link DownloadHelper} for DASH streams. */
|
||||
public final class DashDownloadHelper extends DownloadHelper {
|
||||
|
||||
private final Uri uri;
|
||||
private final DataSource.Factory manifestDataSourceFactory;
|
||||
|
||||
private @MonotonicNonNull DashManifest manifest;
|
||||
|
||||
public DashDownloadHelper(Uri uri, DataSource.Factory manifestDataSourceFactory) {
|
||||
this.uri = uri;
|
||||
this.manifestDataSourceFactory = manifestDataSourceFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareInternal() throws IOException {
|
||||
manifest =
|
||||
ParsingLoadable.load(
|
||||
manifestDataSourceFactory.createDataSource(), new DashManifestParser(), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeriodCount() {
|
||||
Assertions.checkNotNull(manifest);
|
||||
return manifest.getPeriodCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackGroupArray getTrackGroups(int periodIndex) {
|
||||
Assertions.checkNotNull(manifest);
|
||||
List<AdaptationSet> adaptationSets = manifest.getPeriod(periodIndex).adaptationSets;
|
||||
TrackGroup[] trackGroups = new TrackGroup[adaptationSets.size()];
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
List<Representation> representations = adaptationSets.get(i).representations;
|
||||
Format[] formats = new Format[representations.size()];
|
||||
int representationsCount = representations.size();
|
||||
for (int j = 0; j < representationsCount; j++) {
|
||||
formats[j] = representations.get(j).format;
|
||||
}
|
||||
trackGroups[i] = new TrackGroup(formats);
|
||||
}
|
||||
return new TrackGroupArray(trackGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DashDownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
|
||||
return new DashDownloadAction(
|
||||
uri, /* isRemoveAction= */ false, data, toRepresentationKeys(trackKeys));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DashDownloadAction getRemoveAction(@Nullable byte[] data) {
|
||||
return new DashDownloadAction(
|
||||
uri, /* isRemoveAction= */ true, data, Collections.<RepresentationKey>emptyList());
|
||||
}
|
||||
|
||||
private static List<RepresentationKey> toRepresentationKeys(List<TrackKey> trackKeys) {
|
||||
List<RepresentationKey> representationKeys = new ArrayList<>(trackKeys.size());
|
||||
for (int i = 0; i < trackKeys.size(); i++) {
|
||||
TrackKey trackKey = trackKeys.get(i);
|
||||
representationKeys.add(
|
||||
new RepresentationKey(trackKey.periodIndex, trackKey.groupIndex, trackKey.trackIndex));
|
||||
}
|
||||
return representationKeys;
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation 'com.android.support:support-annotations:' + supportLibraryVersion
|
||||
implementation 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||
implementation project(modulePrefix + 'library-core')
|
||||
testImplementation project(modulePrefix + 'testutils-robolectric')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.hls.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.TrackKey;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.RenditionKey;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** A {@link DownloadHelper} for HLS streams. */
|
||||
public final class HlsDownloadHelper extends DownloadHelper {
|
||||
|
||||
private final Uri uri;
|
||||
private final DataSource.Factory manifestDataSourceFactory;
|
||||
|
||||
private @MonotonicNonNull HlsPlaylist<?> playlist;
|
||||
private int[] renditionTypes;
|
||||
|
||||
public HlsDownloadHelper(Uri uri, DataSource.Factory manifestDataSourceFactory) {
|
||||
this.uri = uri;
|
||||
this.manifestDataSourceFactory = manifestDataSourceFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareInternal() throws IOException {
|
||||
DataSource dataSource = manifestDataSourceFactory.createDataSource();
|
||||
playlist = ParsingLoadable.load(dataSource, new HlsPlaylistParser(), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeriodCount() {
|
||||
Assertions.checkNotNull(playlist);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackGroupArray getTrackGroups(int periodIndex) {
|
||||
Assertions.checkNotNull(playlist);
|
||||
if (playlist instanceof HlsMediaPlaylist) {
|
||||
return TrackGroupArray.EMPTY;
|
||||
}
|
||||
// TODO: Generate track groups as in playback. Reverse the mapping in getDownloadAction.
|
||||
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
|
||||
TrackGroup[] trackGroups = new TrackGroup[3];
|
||||
renditionTypes = new int[3];
|
||||
int trackGroupIndex = 0;
|
||||
if (!masterPlaylist.variants.isEmpty()) {
|
||||
renditionTypes[trackGroupIndex] = RenditionKey.TYPE_VARIANT;
|
||||
trackGroups[trackGroupIndex++] = new TrackGroup(toFormats(masterPlaylist.variants));
|
||||
}
|
||||
if (!masterPlaylist.audios.isEmpty()) {
|
||||
renditionTypes[trackGroupIndex] = RenditionKey.TYPE_AUDIO;
|
||||
trackGroups[trackGroupIndex++] = new TrackGroup(toFormats(masterPlaylist.audios));
|
||||
}
|
||||
if (!masterPlaylist.subtitles.isEmpty()) {
|
||||
renditionTypes[trackGroupIndex] = RenditionKey.TYPE_SUBTITLE;
|
||||
trackGroups[trackGroupIndex++] = new TrackGroup(toFormats(masterPlaylist.subtitles));
|
||||
}
|
||||
return new TrackGroupArray(Arrays.copyOf(trackGroups, trackGroupIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HlsDownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
|
||||
Assertions.checkNotNull(renditionTypes);
|
||||
return new HlsDownloadAction(
|
||||
uri, /* isRemoveAction= */ false, data, toRenditionKeys(trackKeys, renditionTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HlsDownloadAction getRemoveAction(@Nullable byte[] data) {
|
||||
return new HlsDownloadAction(
|
||||
uri, /* isRemoveAction= */ true, data, Collections.<RenditionKey>emptyList());
|
||||
}
|
||||
|
||||
private static Format[] toFormats(List<HlsMasterPlaylist.HlsUrl> hlsUrls) {
|
||||
Format[] formats = new Format[hlsUrls.size()];
|
||||
for (int i = 0; i < hlsUrls.size(); i++) {
|
||||
formats[i] = hlsUrls.get(i).format;
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
|
||||
private static List<RenditionKey> toRenditionKeys(List<TrackKey> trackKeys, int[] groups) {
|
||||
List<RenditionKey> representationKeys = new ArrayList<>(trackKeys.size());
|
||||
for (int i = 0; i < trackKeys.size(); i++) {
|
||||
TrackKey trackKey = trackKeys.get(i);
|
||||
representationKeys.add(new RenditionKey(groups[trackKey.groupIndex], trackKey.trackIndex));
|
||||
}
|
||||
return representationKeys;
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation project(modulePrefix + 'library-core')
|
||||
implementation 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||
implementation 'com.android.support:support-annotations:' + supportLibraryVersion
|
||||
testImplementation project(modulePrefix + 'testutils-robolectric')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import com.google.android.exoplayer2.util.UriUtil;
|
|||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -34,7 +33,7 @@ import java.util.UUID;
|
|||
* @see <a href="http://msdn.microsoft.com/en-us/library/ee673436(v=vs.90).aspx">IIS Smooth
|
||||
* Streaming Client Manifest Format</a>
|
||||
*/
|
||||
public class SsManifest implements FilterableManifest<SsManifest, TrackKey> {
|
||||
public class SsManifest implements FilterableManifest<SsManifest, StreamKey> {
|
||||
|
||||
public static final int UNSET_LOOKAHEAD = -1;
|
||||
|
||||
|
|
@ -128,15 +127,16 @@ public class SsManifest implements FilterableManifest<SsManifest, TrackKey> {
|
|||
* @return A copy of this manifest with the selected tracks.
|
||||
* @throws IndexOutOfBoundsException If a key has an invalid index.
|
||||
*/
|
||||
public final SsManifest copy(List<TrackKey> trackKeys) {
|
||||
LinkedList<TrackKey> sortedKeys = new LinkedList<>(trackKeys);
|
||||
@Override
|
||||
public final SsManifest copy(List<StreamKey> trackKeys) {
|
||||
ArrayList<StreamKey> sortedKeys = new ArrayList<>(trackKeys);
|
||||
Collections.sort(sortedKeys);
|
||||
|
||||
StreamElement currentStreamElement = null;
|
||||
List<StreamElement> copiedStreamElements = new ArrayList<>();
|
||||
List<Format> copiedFormats = new ArrayList<>();
|
||||
for (int i = 0; i < sortedKeys.size(); i++) {
|
||||
TrackKey key = sortedKeys.get(i);
|
||||
StreamKey key = sortedKeys.get(i);
|
||||
StreamElement streamElement = streamElements[key.streamElementIndex];
|
||||
if (streamElement != currentStreamElement && currentStreamElement != null) {
|
||||
// We're advancing to a new stream element. Add the current one.
|
||||
|
|
|
|||
|
|
@ -20,15 +20,13 @@ import android.os.Parcelable;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Uniquely identifies a track in a {@link SsManifest}.
|
||||
*/
|
||||
public final class TrackKey implements Parcelable, Comparable<TrackKey> {
|
||||
/** Uniquely identifies a track in a {@link SsManifest}. */
|
||||
public final class StreamKey implements Parcelable, Comparable<StreamKey> {
|
||||
|
||||
public final int streamElementIndex;
|
||||
public final int trackIndex;
|
||||
|
||||
public TrackKey(int streamElementIndex, int trackIndex) {
|
||||
public StreamKey(int streamElementIndex, int trackIndex) {
|
||||
this.streamElementIndex = streamElementIndex;
|
||||
this.trackIndex = trackIndex;
|
||||
}
|
||||
|
|
@ -47,7 +45,7 @@ public final class TrackKey implements Parcelable, Comparable<TrackKey> {
|
|||
return false;
|
||||
}
|
||||
|
||||
TrackKey that = (TrackKey) o;
|
||||
StreamKey that = (StreamKey) o;
|
||||
return streamElementIndex == that.streamElementIndex && trackIndex == that.trackIndex;
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +59,7 @@ public final class TrackKey implements Parcelable, Comparable<TrackKey> {
|
|||
// Comparable implementation.
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull TrackKey o) {
|
||||
public int compareTo(@NonNull StreamKey o) {
|
||||
int result = streamElementIndex - o.streamElementIndex;
|
||||
if (result == 0) {
|
||||
result = trackIndex - o.trackIndex;
|
||||
|
|
@ -82,15 +80,16 @@ public final class TrackKey implements Parcelable, Comparable<TrackKey> {
|
|||
dest.writeInt(trackIndex);
|
||||
}
|
||||
|
||||
public static final Creator<TrackKey> CREATOR = new Creator<TrackKey>() {
|
||||
@Override
|
||||
public TrackKey createFromParcel(Parcel in) {
|
||||
return new TrackKey(in.readInt(), in.readInt());
|
||||
}
|
||||
public static final Creator<StreamKey> CREATOR =
|
||||
new Creator<StreamKey>() {
|
||||
@Override
|
||||
public StreamKey createFromParcel(Parcel in) {
|
||||
return new StreamKey(in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackKey[] newArray(int size) {
|
||||
return new TrackKey[size];
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public StreamKey[] newArray(int size) {
|
||||
return new StreamKey[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -20,29 +20,29 @@ import android.support.annotation.Nullable;
|
|||
import com.google.android.exoplayer2.offline.DownloadAction;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
import com.google.android.exoplayer2.offline.SegmentDownloadAction;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.TrackKey;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.StreamKey;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** An action to download or remove downloaded SmoothStreaming streams. */
|
||||
public final class SsDownloadAction extends SegmentDownloadAction<TrackKey> {
|
||||
public final class SsDownloadAction extends SegmentDownloadAction<StreamKey> {
|
||||
|
||||
private static final String TYPE = "ss";
|
||||
private static final int VERSION = 0;
|
||||
|
||||
public static final Deserializer DESERIALIZER =
|
||||
new SegmentDownloadActionDeserializer<TrackKey>(TYPE, VERSION) {
|
||||
new SegmentDownloadActionDeserializer<StreamKey>(TYPE, VERSION) {
|
||||
|
||||
@Override
|
||||
protected TrackKey readKey(DataInputStream input) throws IOException {
|
||||
return new TrackKey(input.readInt(), input.readInt());
|
||||
protected StreamKey readKey(DataInputStream input) throws IOException {
|
||||
return new StreamKey(input.readInt(), input.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DownloadAction createDownloadAction(
|
||||
Uri uri, boolean isRemoveAction, byte[] data, List<TrackKey> keys) {
|
||||
Uri uri, boolean isRemoveAction, byte[] data, List<StreamKey> keys) {
|
||||
return new SsDownloadAction(uri, isRemoveAction, data, keys);
|
||||
}
|
||||
};
|
||||
|
|
@ -55,7 +55,7 @@ public final class SsDownloadAction extends SegmentDownloadAction<TrackKey> {
|
|||
* removeAction} is true, {@code keys} must be empty.
|
||||
*/
|
||||
public SsDownloadAction(
|
||||
Uri uri, boolean isRemoveAction, @Nullable byte[] data, List<TrackKey> keys) {
|
||||
Uri uri, boolean isRemoveAction, @Nullable byte[] data, List<StreamKey> keys) {
|
||||
super(TYPE, VERSION, uri, isRemoveAction, data, keys);
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ public final class SsDownloadAction extends SegmentDownloadAction<TrackKey> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void writeKey(DataOutputStream output, TrackKey key) throws IOException {
|
||||
protected void writeKey(DataOutputStream output, StreamKey key) throws IOException {
|
||||
output.writeInt(key.streamElementIndex);
|
||||
output.writeInt(key.trackIndex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.smoothstreaming.offline;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.TrackKey;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.StreamKey;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** A {@link DownloadHelper} for SmoothStreaming streams. */
|
||||
public final class SsDownloadHelper extends DownloadHelper {
|
||||
|
||||
private final Uri uri;
|
||||
private final DataSource.Factory manifestDataSourceFactory;
|
||||
|
||||
private @MonotonicNonNull SsManifest manifest;
|
||||
|
||||
public SsDownloadHelper(Uri uri, DataSource.Factory manifestDataSourceFactory) {
|
||||
this.uri = uri;
|
||||
this.manifestDataSourceFactory = manifestDataSourceFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareInternal() throws IOException {
|
||||
DataSource dataSource = manifestDataSourceFactory.createDataSource();
|
||||
manifest = ParsingLoadable.load(dataSource, new SsManifestParser(), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeriodCount() {
|
||||
Assertions.checkNotNull(manifest);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackGroupArray getTrackGroups(int periodIndex) {
|
||||
Assertions.checkNotNull(manifest);
|
||||
SsManifest.StreamElement[] streamElements = manifest.streamElements;
|
||||
TrackGroup[] trackGroups = new TrackGroup[streamElements.length];
|
||||
for (int i = 0; i < streamElements.length; i++) {
|
||||
trackGroups[i] = new TrackGroup(streamElements[i].formats);
|
||||
}
|
||||
return new TrackGroupArray(trackGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SsDownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
|
||||
return new SsDownloadAction(uri, /* isRemoveAction= */ false, data, toStreamKeys(trackKeys));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SsDownloadAction getRemoveAction(@Nullable byte[] data) {
|
||||
return new SsDownloadAction(
|
||||
uri, /* isRemoveAction= */ true, data, Collections.<StreamKey>emptyList());
|
||||
}
|
||||
|
||||
private static List<StreamKey> toStreamKeys(List<TrackKey> trackKeys) {
|
||||
List<StreamKey> representationKeys = new ArrayList<>(trackKeys.size());
|
||||
for (int i = 0; i < trackKeys.size(); i++) {
|
||||
TrackKey trackKey = trackKeys.get(i);
|
||||
representationKeys.add(new StreamKey(trackKey.groupIndex, trackKey.trackIndex));
|
||||
}
|
||||
return representationKeys;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
|
|||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsUtil;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.TrackKey;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.StreamKey;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
|
|
@ -46,7 +46,7 @@ import java.util.List;
|
|||
* new SsDownloader(
|
||||
* manifestUrl,
|
||||
* constructorHelper,
|
||||
* Collections.singletonList(new TrackKey(0, 0)));
|
||||
* Collections.singletonList(new StreamKey(0, 0)));
|
||||
* // Perform the download.
|
||||
* ssDownloader.download();
|
||||
* // Access downloaded data using CacheDataSource
|
||||
|
|
@ -54,11 +54,11 @@ import java.util.List;
|
|||
* new CacheDataSource(cache, factory.createDataSource(), CacheDataSource.FLAG_BLOCK_ON_CACHE);
|
||||
* }</pre>
|
||||
*/
|
||||
public final class SsDownloader extends SegmentDownloader<SsManifest, TrackKey> {
|
||||
public final class SsDownloader extends SegmentDownloader<SsManifest, StreamKey> {
|
||||
|
||||
/** @see SegmentDownloader#SegmentDownloader(Uri, DownloaderConstructorHelper, List) */
|
||||
public SsDownloader(
|
||||
Uri manifestUri, DownloaderConstructorHelper constructorHelper, List<TrackKey> trackKeys) {
|
||||
Uri manifestUri, DownloaderConstructorHelper constructorHelper, List<StreamKey> trackKeys) {
|
||||
super(SsUtil.fixManifestUri(manifestUri), constructorHelper, trackKeys);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ public class SsManifestTest {
|
|||
SsManifest sourceManifest =
|
||||
newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1]));
|
||||
|
||||
List<TrackKey> keys = Arrays.asList(new TrackKey(0, 0), new TrackKey(0, 2), new TrackKey(1, 0));
|
||||
List<StreamKey> keys =
|
||||
Arrays.asList(new StreamKey(0, 0), new StreamKey(0, 2), new StreamKey(1, 0));
|
||||
// Keys don't need to be in any particular order
|
||||
Collections.shuffle(keys, new Random(0));
|
||||
|
||||
|
|
@ -62,7 +63,7 @@ public class SsManifestTest {
|
|||
SsManifest sourceManifest =
|
||||
newSsManifest(newStreamElement("1", formats[0]), newStreamElement("2", formats[1]));
|
||||
|
||||
List<TrackKey> keys = Arrays.asList(new TrackKey(1, 0));
|
||||
List<StreamKey> keys = Arrays.asList(new StreamKey(1, 0));
|
||||
// Keys don't need to be in any particular order
|
||||
Collections.shuffle(keys, new Random(0));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue