Use default Deserializers if non given to DownloadManager

Also moved shared code to SegmentDownloadAction between its subclasses.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=202294880
This commit is contained in:
eguven 2018-06-27 05:48:04 -07:00 committed by Oliver Woodman
parent f2ce0d8981
commit 93cbae5bff
31 changed files with 785 additions and 208 deletions

View File

@ -37,6 +37,7 @@
CommentFrame to InternalFrame for frames with gapless metadata in MP4.
* Allow setting the `Looper`, which is used to access the player, in
`ExoPlayerFactory` ([#4278](https://github.com/google/ExoPlayer/issues/4278)).
* Use default Deserializers if non given to DownloadManager.
* Deprecate `Player.DefaultEventListener` as selective listener overrides can
be directly made with the `Player.EventListener` interface.
* Deprecate `DefaultAnalyticsListener` as selective listener overrides can be

View File

@ -16,13 +16,8 @@
package com.google.android.exoplayer2.demo;
import android.app.Application;
import com.google.android.exoplayer2.offline.DownloadAction.Deserializer;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.ProgressiveDownloadAction;
import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction;
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction;
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
@ -46,13 +41,6 @@ public class DemoApplication extends Application {
private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions";
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
private static final int MAX_SIMULTANEOUS_DOWNLOADS = 2;
private static final Deserializer[] DOWNLOAD_DESERIALIZERS =
new Deserializer[] {
DashDownloadAction.DESERIALIZER,
HlsDownloadAction.DESERIALIZER,
SsDownloadAction.DESERIALIZER,
ProgressiveDownloadAction.DESERIALIZER
};
protected String userAgent;
@ -105,14 +93,12 @@ public class DemoApplication extends Application {
downloaderConstructorHelper,
MAX_SIMULTANEOUS_DOWNLOADS,
DownloadManager.DEFAULT_MIN_RETRY_COUNT,
new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE),
DOWNLOAD_DESERIALIZERS);
new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE));
downloadTracker =
new DownloadTracker(
/* context= */ this,
buildDataSourceFactory(/* listener= */ null),
new File(getDownloadDirectory(), DOWNLOAD_TRACKER_ACTION_FILE),
DOWNLOAD_DESERIALIZERS);
new File(getDownloadDirectory(), DOWNLOAD_TRACKER_ACTION_FILE));
downloadManager.addListener(downloadTracker);
}
}

View File

@ -36,7 +36,6 @@ import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.ProgressiveDownloadHelper;
import com.google.android.exoplayer2.offline.SegmentDownloadAction;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.offline.TrackKey;
import com.google.android.exoplayer2.source.TrackGroup;
@ -86,7 +85,7 @@ public class DownloadTracker implements DownloadManager.Listener {
Context context,
DataSource.Factory dataSourceFactory,
File actionFile,
DownloadAction.Deserializer[] deserializers) {
DownloadAction.Deserializer... deserializers) {
this.context = context.getApplicationContext();
this.dataSourceFactory = dataSourceFactory;
this.actionFile = new ActionFile(actionFile);
@ -96,7 +95,8 @@ public class DownloadTracker implements DownloadManager.Listener {
HandlerThread actionFileWriteThread = new HandlerThread("DownloadTracker");
actionFileWriteThread.start();
actionFileWriteHandler = new Handler(actionFileWriteThread.getLooper());
loadTrackedActions(deserializers);
loadTrackedActions(
deserializers.length > 0 ? deserializers : DownloadAction.getDefaultDeserializers());
}
public void addListener(Listener listener) {
@ -116,11 +116,7 @@ public class DownloadTracker implements DownloadManager.Listener {
if (!trackedDownloadStates.containsKey(uri)) {
return Collections.emptyList();
}
DownloadAction action = trackedDownloadStates.get(uri);
if (action instanceof SegmentDownloadAction) {
return ((SegmentDownloadAction) action).keys;
}
return Collections.emptyList();
return trackedDownloadStates.get(uri).getKeys();
}
public void toggleDownload(Activity activity, String name, Uri uri, String extension) {

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.offline;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@ -24,6 +25,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** Contains the necessary parameters for a download or remove action. */
public abstract class DownloadAction {
@ -50,6 +53,48 @@ public abstract class DownloadAction {
throws IOException;
}
private static @Nullable Deserializer[] defaultDeserializers;
/** Returns available default {@link Deserializer}s. */
public static synchronized Deserializer[] getDefaultDeserializers() {
if (defaultDeserializers != null) {
return defaultDeserializers;
}
Deserializer[] deserializers = new Deserializer[4];
int count = 0;
deserializers[count++] = ProgressiveDownloadAction.DESERIALIZER;
Class<?> clazz;
// Full class names used for constructor args so the LINT rule triggers if any of them move.
try {
// LINT.IfChange
clazz = Class.forName("com.google.android.exoplayer2.source.dash.offline.DashDownloadAction");
// LINT.ThenChange(../../../../../../../../../dash/proguard-rules.txt)
deserializers[count++] = getDeserializer(clazz);
} catch (Exception e) {
// Do nothing.
}
try {
// LINT.IfChange
clazz = Class.forName("com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction");
// LINT.ThenChange(../../../../../../../../../hls/proguard-rules.txt)
deserializers[count++] = getDeserializer(clazz);
} catch (Exception e) {
// Do nothing.
}
try {
// LINT.IfChange
clazz =
Class.forName(
"com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction");
// LINT.ThenChange(../../../../../../../../../smoothstreaming/proguard-rules.txt)
deserializers[count++] = getDeserializer(clazz);
} catch (Exception e) {
// Do nothing.
}
defaultDeserializers = Arrays.copyOf(Assertions.checkNotNull(deserializers), count);
return defaultDeserializers;
}
/**
* Deserializes one action that was serialized with {@link #serializeToStream(DownloadAction,
* OutputStream)} from the {@code input}, using the {@link Deserializer}s that supports the
@ -132,11 +177,16 @@ public abstract class DownloadAction {
return uri.equals(other.uri);
}
/** Returns keys of tracks to be downloaded. */
public List<StreamKey> getKeys() {
return Collections.emptyList();
}
/** Serializes itself into the {@code output}. */
protected abstract void writeToStream(DataOutputStream output) throws IOException;
/** Creates a {@link Downloader} with the given parameters. */
protected abstract Downloader createDownloader(
public abstract Downloader createDownloader(
DownloaderConstructorHelper downloaderConstructorHelper);
@Override
@ -160,4 +210,9 @@ public abstract class DownloadAction {
return result;
}
private static Deserializer getDeserializer(Class<?> clazz)
throws NoSuchFieldException, IllegalAccessException {
Object value = clazz.getDeclaredField("DESERIALIZER").get(null);
return (Deserializer) Assertions.checkNotNull(value);
}
}

View File

@ -108,7 +108,8 @@ public final class DownloadManager {
* @param upstreamDataSourceFactory A {@link DataSource.Factory} for creating data sources for
* downloading upstream data.
* @param actionSaveFile File to save active actions.
* @param deserializers Used to deserialize {@link DownloadAction}s.
* @param deserializers Used to deserialize {@link DownloadAction}s. If empty, {@link
* DownloadAction#getDefaultDeserializers()} is used instead.
*/
public DownloadManager(
Cache cache,
@ -127,7 +128,8 @@ public final class DownloadManager {
* @param constructorHelper A {@link DownloaderConstructorHelper} to create {@link Downloader}s
* for downloading data.
* @param actionFile The file in which active actions are saved.
* @param deserializers Used to deserialize {@link DownloadAction}s.
* @param deserializers Used to deserialize {@link DownloadAction}s. If empty, {@link
* DownloadAction#getDefaultDeserializers()} is used instead.
*/
public DownloadManager(
DownloaderConstructorHelper constructorHelper,
@ -149,7 +151,8 @@ public final class DownloadManager {
* @param maxSimultaneousDownloads The maximum number of simultaneous download tasks.
* @param minRetryCount The minimum number of times a task must be retried before failing.
* @param actionFile The file in which active actions are saved.
* @param deserializers Used to deserialize {@link DownloadAction}s.
* @param deserializers Used to deserialize {@link DownloadAction}s. If empty, {@link
* DownloadAction#getDefaultDeserializers()} is used instead.
*/
public DownloadManager(
DownloaderConstructorHelper constructorHelper,
@ -157,13 +160,12 @@ public final class DownloadManager {
int minRetryCount,
File actionFile,
Deserializer... deserializers) {
Assertions.checkArgument(deserializers.length > 0, "At least one Deserializer is required.");
this.downloaderConstructorHelper = constructorHelper;
this.maxActiveDownloadTasks = maxSimultaneousDownloads;
this.minRetryCount = minRetryCount;
this.actionFile = new ActionFile(actionFile);
this.deserializers = deserializers;
this.deserializers =
deserializers.length > 0 ? deserializers : DownloadAction.getDefaultDeserializers();
this.downloadsStopped = true;
tasks = new ArrayList<>();

View File

@ -44,7 +44,33 @@ public final class ProgressiveDownloadAction extends DownloadAction {
}
};
public final @Nullable String customCacheKey;
private final @Nullable String customCacheKey;
/**
* Creates a progressive stream download action.
*
* @param uri Uri of the data to be downloaded.
* @param data Optional custom data for this action.
* @param customCacheKey A custom key that uniquely identifies the original stream. If not null it
* is used for cache indexing.
*/
public static ProgressiveDownloadAction createDownloadAction(
Uri uri, @Nullable byte[] data, @Nullable String customCacheKey) {
return new ProgressiveDownloadAction(uri, /* isRemoveAction= */ false, data, customCacheKey);
}
/**
* Creates a progressive stream remove action.
*
* @param uri Uri of the data to be removed.
* @param data Optional custom data for this action.
* @param customCacheKey A custom key that uniquely identifies the original stream. If not null it
* is used for cache indexing.
*/
public static ProgressiveDownloadAction createRemoveAction(
Uri uri, @Nullable byte[] data, @Nullable String customCacheKey) {
return new ProgressiveDownloadAction(uri, /* isRemoveAction= */ true, data, customCacheKey);
}
/**
* @param uri Uri of the data to be downloaded.
@ -52,7 +78,10 @@ public final class ProgressiveDownloadAction extends DownloadAction {
* @param data Optional custom data for this action.
* @param customCacheKey A custom key that uniquely identifies the original stream. If not null it
* is used for cache indexing.
* @deprecated Use {@link #createDownloadAction(Uri, byte[], String)} or {@link
* #createRemoveAction(Uri, byte[], String)}.
*/
@Deprecated
public ProgressiveDownloadAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, @Nullable String customCacheKey) {
super(TYPE, VERSION, uri, isRemoveAction, data);
@ -60,7 +89,7 @@ public final class ProgressiveDownloadAction extends DownloadAction {
}
@Override
protected ProgressiveDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
public ProgressiveDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
return new ProgressiveDownloader(uri, customCacheKey, constructorHelper);
}
@ -105,4 +134,5 @@ public final class ProgressiveDownloadAction extends DownloadAction {
private String getCacheKey() {
return customCacheKey != null ? customCacheKey : CacheUtil.generateKey(uri);
}
}

View File

@ -51,12 +51,13 @@ public final class ProgressiveDownloadHelper extends DownloadHelper {
}
@Override
public DownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
return new ProgressiveDownloadAction(uri, false, data, customCacheKey);
public ProgressiveDownloadAction getDownloadAction(
@Nullable byte[] data, List<TrackKey> trackKeys) {
return ProgressiveDownloadAction.createDownloadAction(uri, data, customCacheKey);
}
@Override
public DownloadAction getRemoveAction(@Nullable byte[] data) {
return new ProgressiveDownloadAction(uri, true, data, customCacheKey);
public ProgressiveDownloadAction getRemoveAction(@Nullable byte[] data) {
return ProgressiveDownloadAction.createRemoveAction(uri, data, customCacheKey);
}
}

View File

@ -46,13 +46,18 @@ public abstract class SegmentDownloadAction extends DownloadAction {
int keyCount = input.readInt();
List<StreamKey> keys = new ArrayList<>();
for (int i = 0; i < keyCount; i++) {
keys.add(readKey(input));
keys.add(readKey(version, input));
}
return createDownloadAction(uri, isRemoveAction, data, keys);
}
/** Deserializes a key from the {@code input}. */
protected abstract StreamKey readKey(DataInputStream input) throws IOException;
protected StreamKey readKey(int version, DataInputStream input) throws IOException {
int periodIndex = input.readInt();
int groupIndex = input.readInt();
int trackIndex = input.readInt();
return new StreamKey(periodIndex, groupIndex, trackIndex);
}
/** Returns a {@link DownloadAction}. */
protected abstract DownloadAction createDownloadAction(
@ -88,6 +93,11 @@ public abstract class SegmentDownloadAction extends DownloadAction {
}
}
@Override
public List<StreamKey> getKeys() {
return keys;
}
@Override
public final void writeToStream(DataOutputStream output) throws IOException {
output.writeUTF(uri.toString());
@ -100,9 +110,6 @@ public abstract class SegmentDownloadAction extends DownloadAction {
}
}
/** Serializes the {@code key} into the {@code output}. */
protected abstract void writeKey(DataOutputStream output, StreamKey key) throws IOException;
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
@ -122,4 +129,10 @@ public abstract class SegmentDownloadAction extends DownloadAction {
return result;
}
/** Serializes the {@code key} into the {@code output}. */
private void writeKey(DataOutputStream output, StreamKey key) throws IOException {
output.writeInt(key.periodIndex);
output.writeInt(key.groupIndex);
output.writeInt(key.trackIndex);
}
}

View File

@ -33,9 +33,7 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Unit tests for {@link ProgressiveDownloadAction}.
*/
/** Unit tests for {@link ActionFile}. */
@RunWith(RobolectricTestRunner.class)
public class ActionFileTest {
@ -258,7 +256,7 @@ public class ActionFileTest {
}
@Override
protected Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) {
public Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) {
return null;
}

View File

@ -510,7 +510,7 @@ public class DownloadManagerTest {
}
@Override
protected Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) {
public Downloader createDownloader(DownloaderConstructorHelper downloaderConstructorHelper) {
return downloader;
}

View File

@ -32,9 +32,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
/**
* Unit tests for {@link ProgressiveDownloadAction}.
*/
/** Unit tests for {@link ProgressiveDownloadAction}. */
@RunWith(RobolectricTestRunner.class)
public class ProgressiveDownloadActionTest {
@ -49,112 +47,109 @@ public class ProgressiveDownloadActionTest {
@Test
public void testDownloadActionIsNotRemoveAction() throws Exception {
ProgressiveDownloadAction action = new ProgressiveDownloadAction(uri1, false, null, null);
DownloadAction action = createDownloadAction(uri1, null);
assertThat(action.isRemoveAction).isFalse();
}
@Test
public void testRemoveActionisRemoveAction() throws Exception {
ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, true, null, null);
DownloadAction action2 = createRemoveAction(uri1, null);
assertThat(action2.isRemoveAction).isTrue();
}
@Test
public void testCreateDownloader() throws Exception {
MockitoAnnotations.initMocks(this);
ProgressiveDownloadAction action = new ProgressiveDownloadAction(uri1, false, null, null);
DownloaderConstructorHelper constructorHelper = new DownloaderConstructorHelper(
Mockito.mock(Cache.class), DummyDataSource.FACTORY);
DownloadAction action = createDownloadAction(uri1, null);
DownloaderConstructorHelper constructorHelper =
new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY);
assertThat(action.createDownloader(constructorHelper)).isNotNull();
}
@Test
public void testSameUriCacheKeyDifferentAction_IsSameMedia() throws Exception {
ProgressiveDownloadAction action1 = new ProgressiveDownloadAction(uri1, true, null, null);
ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, false, null, null);
DownloadAction action1 = createRemoveAction(uri1, null);
DownloadAction action2 = createDownloadAction(uri1, null);
assertSameMedia(action1, action2);
}
@Test
public void testNullCacheKeyDifferentUriAction_IsNotSameMedia() throws Exception {
ProgressiveDownloadAction action3 = new ProgressiveDownloadAction(uri2, true, null, null);
ProgressiveDownloadAction action4 = new ProgressiveDownloadAction(uri1, false, null, null);
DownloadAction action3 = createRemoveAction(uri2, null);
DownloadAction action4 = createDownloadAction(uri1, null);
assertNotSameMedia(action3, action4);
}
@Test
public void testSameCacheKeyDifferentUriAction_IsSameMedia() throws Exception {
ProgressiveDownloadAction action5 = new ProgressiveDownloadAction(uri2, true, null, "key");
ProgressiveDownloadAction action6 = new ProgressiveDownloadAction(uri1, false, null, "key");
DownloadAction action5 = createRemoveAction(uri2, "key");
DownloadAction action6 = createDownloadAction(uri1, "key");
assertSameMedia(action5, action6);
}
@Test
public void testSameUriDifferentCacheKeyAction_IsNotSameMedia() throws Exception {
ProgressiveDownloadAction action7 = new ProgressiveDownloadAction(uri1, true, null, "key");
ProgressiveDownloadAction action8 = new ProgressiveDownloadAction(uri1, false, null, "key2");
DownloadAction action7 = createRemoveAction(uri1, "key");
DownloadAction action8 = createDownloadAction(uri1, "key2");
assertNotSameMedia(action7, action8);
}
@Test
public void testSameUriNullCacheKeyAction_IsNotSameMedia() throws Exception {
ProgressiveDownloadAction action1 = new ProgressiveDownloadAction(uri1, true, null, "key");
ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, false, null, null);
DownloadAction action1 = createRemoveAction(uri1, "key");
DownloadAction action2 = createDownloadAction(uri1, null);
assertNotSameMedia(action1, action2);
}
@Test
public void testEquals() throws Exception {
ProgressiveDownloadAction action1 = new ProgressiveDownloadAction(uri1, true, null, null);
DownloadAction action1 = createRemoveAction(uri1, null);
assertThat(action1.equals(action1)).isTrue();
ProgressiveDownloadAction action2 = new ProgressiveDownloadAction(uri1, true, null, null);
ProgressiveDownloadAction action3 = new ProgressiveDownloadAction(uri1, true, null, null);
DownloadAction action2 = createRemoveAction(uri1, null);
DownloadAction action3 = createRemoveAction(uri1, null);
assertThat(action2.equals(action3)).isTrue();
ProgressiveDownloadAction action4 = new ProgressiveDownloadAction(uri1, true, null, null);
ProgressiveDownloadAction action5 = new ProgressiveDownloadAction(uri1, false, null, null);
DownloadAction action4 = createRemoveAction(uri1, null);
DownloadAction action5 = createDownloadAction(uri1, null);
assertThat(action4.equals(action5)).isFalse();
ProgressiveDownloadAction action6 = new ProgressiveDownloadAction(uri1, true, null, null);
ProgressiveDownloadAction action7 = new ProgressiveDownloadAction(uri1, true, null, "key");
DownloadAction action6 = createRemoveAction(uri1, null);
DownloadAction action7 = createRemoveAction(uri1, "key");
assertThat(action6.equals(action7)).isFalse();
ProgressiveDownloadAction action8 = new ProgressiveDownloadAction(uri1, true, null, "key2");
ProgressiveDownloadAction action9 = new ProgressiveDownloadAction(uri1, true, null, "key");
DownloadAction action8 = createRemoveAction(uri1, "key2");
DownloadAction action9 = createRemoveAction(uri1, "key");
assertThat(action8.equals(action9)).isFalse();
ProgressiveDownloadAction action10 = new ProgressiveDownloadAction(uri1, true, null, null);
ProgressiveDownloadAction action11 = new ProgressiveDownloadAction(uri2, true, null, null);
DownloadAction action10 = createRemoveAction(uri1, null);
DownloadAction action11 = createRemoveAction(uri2, null);
assertThat(action10.equals(action11)).isFalse();
}
@Test
public void testSerializerGetType() throws Exception {
ProgressiveDownloadAction action = new ProgressiveDownloadAction(uri1, false, null, null);
DownloadAction action = createDownloadAction(uri1, null);
assertThat(action.type).isNotNull();
}
@Test
public void testSerializerWriteRead() throws Exception {
doTestSerializationRoundTrip(new ProgressiveDownloadAction(uri1, false, null, null));
doTestSerializationRoundTrip(new ProgressiveDownloadAction(uri2, true, null, "key"));
doTestSerializationRoundTrip(createDownloadAction(uri1, null));
doTestSerializationRoundTrip(createRemoveAction(uri2, "key"));
}
private void assertSameMedia(
ProgressiveDownloadAction action1, ProgressiveDownloadAction action2) {
private void assertSameMedia(DownloadAction action1, DownloadAction action2) {
assertThat(action1.isSameMedia(action2)).isTrue();
assertThat(action2.isSameMedia(action1)).isTrue();
}
private void assertNotSameMedia(
ProgressiveDownloadAction action1, ProgressiveDownloadAction action2) {
private void assertNotSameMedia(DownloadAction action1, DownloadAction action2) {
assertThat(action1.isSameMedia(action2)).isFalse();
assertThat(action2.isSameMedia(action1)).isFalse();
}
private static void doTestSerializationRoundTrip(ProgressiveDownloadAction action)
throws IOException {
private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(out);
DownloadAction.serializeToStream(action, output);
@ -168,4 +163,11 @@ public class ProgressiveDownloadActionTest {
assertThat(action2).isEqualTo(action);
}
private static DownloadAction createDownloadAction(Uri uri1, String customCacheKey) {
return ProgressiveDownloadAction.createDownloadAction(uri1, null, customCacheKey);
}
private static DownloadAction createRemoveAction(Uri uri1, String customCacheKey) {
return ProgressiveDownloadAction.createRemoveAction(uri1, null, customCacheKey);
}
}

View File

@ -26,6 +26,7 @@ android {
defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
consumerProguardFiles 'proguard-rules.txt'
}
buildTypes {

View File

@ -0,0 +1,7 @@
# Proguard rules specific to the dash module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.dash.offline.DashDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.dash.offline.DashDownloadAction {
static ** DESERIALIZER;
}

View File

@ -21,9 +21,7 @@ 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.offline.StreamKey;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/** An action to download or remove downloaded DASH streams. */
@ -34,15 +32,6 @@ public final class DashDownloadAction extends SegmentDownloadAction {
public static final Deserializer DESERIALIZER =
new SegmentDownloadActionDeserializer(TYPE, VERSION) {
@Override
protected StreamKey readKey(DataInputStream input) throws IOException {
int periodIndex = input.readInt();
int groupIndex = input.readInt();
int trackIndex = input.readInt();
return new StreamKey(periodIndex, groupIndex, trackIndex);
}
@Override
protected DownloadAction createDownloadAction(
Uri uri, boolean isRemoveAction, byte[] data, List<StreamKey> keys) {
@ -50,28 +39,46 @@ public final class DashDownloadAction extends SegmentDownloadAction {
}
};
/**
* Creates a DASH download action.
*
* @param uri The URI of the media to be downloaded.
* @param data Optional custom data for this action. If {@code null} an empty array will be used.
* @param keys Keys of tracks to be downloaded. If empty, all tracks will be downloaded.
*/
public static DashDownloadAction createDownloadAction(
Uri uri, @Nullable byte[] data, List<StreamKey> keys) {
return new DashDownloadAction(uri, /* isRemoveAction= */ false, data, keys);
}
/**
* Creates a DASH remove action.
*
* @param uri The URI of the media to be removed.
* @param data Optional custom data for this action. If {@code null} an empty array will be used.
*/
public static DashDownloadAction createRemoveAction(Uri uri, @Nullable byte[] data) {
return new DashDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList());
}
/**
* @param uri The DASH manifest URI.
* @param isRemoveAction Whether the data will be removed. If {@code false} it will be downloaded.
* @param data Optional custom data for this action.
* @param keys Keys of representations to be downloaded. If empty, all representations are
* downloaded. If {@code removeAction} is true, {@code keys} must be empty.
* @deprecated Use {@link #createDownloadAction(Uri, byte[], List)} or {@link
* #createRemoveAction(Uri, byte[])}.
*/
@Deprecated
public DashDownloadAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, List<StreamKey> keys) {
super(TYPE, VERSION, uri, isRemoveAction, data, keys);
}
@Override
protected DashDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
public DashDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
return new DashDownloader(uri, keys, constructorHelper);
}
@Override
protected void writeKey(DataOutputStream output, StreamKey key) throws IOException {
output.writeInt(key.periodIndex);
output.writeInt(key.groupIndex);
output.writeInt(key.trackIndex);
}
}

View File

@ -33,7 +33,6 @@ 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;
@ -88,12 +87,12 @@ public final class DashDownloadHelper extends DownloadHelper {
@Override
public DashDownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
return new DashDownloadAction(uri, /* isRemoveAction= */ false, data, toStreamKeys(trackKeys));
return DashDownloadAction.createDownloadAction(uri, data, toStreamKeys(trackKeys));
}
@Override
public DashDownloadAction getRemoveAction(@Nullable byte[] data) {
return new DashDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList());
return DashDownloadAction.createRemoveAction(uri, data);
}
private static List<StreamKey> toStreamKeys(List<TrackKey> trackKeys) {

View File

@ -0,0 +1,7 @@
# Proguard rules specific to the dash module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.dash.offline.DashDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.dash.offline.DashDownloadAction {
static ** DESERIALIZER;
}

View File

@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source.dash.offline;
import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
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.StreamKey;
@ -38,9 +37,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
/**
* Unit tests for {@link DashDownloadAction}.
*/
/** Unit tests for {@link DashDownloadAction}. */
@RunWith(RobolectricTestRunner.class)
public class DashDownloadActionTest {
@ -55,130 +52,106 @@ public class DashDownloadActionTest {
@Test
public void testDownloadActionIsNotRemoveAction() {
DashDownloadAction action = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DownloadAction action = createDownloadAction(uri1);
assertThat(action.isRemoveAction).isFalse();
}
@Test
public void testRemoveActionisRemoveAction() {
DashDownloadAction action2 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null);
public void testRemoveActionIsRemoveAction() {
DownloadAction action2 = createRemoveAction(uri1);
assertThat(action2.isRemoveAction).isTrue();
}
@Test
public void testCreateDownloader() {
MockitoAnnotations.initMocks(this);
DashDownloadAction action = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DownloaderConstructorHelper constructorHelper = new DownloaderConstructorHelper(
Mockito.mock(Cache.class), DummyDataSource.FACTORY);
DownloadAction action = createDownloadAction(uri1);
DownloaderConstructorHelper constructorHelper =
new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY);
assertThat(action.createDownloader(constructorHelper)).isNotNull();
}
@Test
public void testSameUriDifferentAction_IsSameMedia() {
DashDownloadAction action1 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null);
DashDownloadAction action2 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DownloadAction action1 = createRemoveAction(uri1);
DownloadAction action2 = createDownloadAction(uri1);
assertThat(action1.isSameMedia(action2)).isTrue();
}
@Test
public void testDifferentUriAndAction_IsNotSameMedia() {
DashDownloadAction action3 = newAction(uri2, /* isRemoveAction= */ true, /* data= */ null);
DashDownloadAction action4 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DownloadAction action3 = createRemoveAction(uri2);
DownloadAction action4 = createDownloadAction(uri1);
assertThat(action3.isSameMedia(action4)).isFalse();
}
@SuppressWarnings("EqualsWithItself")
@Test
public void testEquals() {
DashDownloadAction action1 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null);
DownloadAction action1 = createRemoveAction(uri1);
assertThat(action1.equals(action1)).isTrue();
DashDownloadAction action2 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null);
DashDownloadAction action3 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null);
DownloadAction action2 = createRemoveAction(uri1);
DownloadAction action3 = createRemoveAction(uri1);
assertEqual(action2, action3);
DashDownloadAction action4 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null);
DashDownloadAction action5 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DownloadAction action4 = createRemoveAction(uri1);
DownloadAction action5 = createDownloadAction(uri1);
assertNotEqual(action4, action5);
DashDownloadAction action6 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DashDownloadAction action7 =
newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(0, 0, 0));
DownloadAction action6 = createDownloadAction(uri1);
DownloadAction action7 = createDownloadAction(uri1, new StreamKey(0, 0, 0));
assertNotEqual(action6, action7);
DashDownloadAction action8 =
newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(1, 1, 1));
DashDownloadAction action9 =
newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(0, 0, 0));
DownloadAction action8 = createDownloadAction(uri1, new StreamKey(1, 1, 1));
DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0, 0));
assertNotEqual(action8, action9);
DashDownloadAction action10 = newAction(uri1, /* isRemoveAction= */ true, /* data= */ null);
DashDownloadAction action11 = newAction(uri2, /* isRemoveAction= */ true, /* data= */ null);
DownloadAction action10 = createRemoveAction(uri1);
DownloadAction action11 = createRemoveAction(uri2);
assertNotEqual(action10, action11);
DashDownloadAction action12 =
newAction(
uri1,
/* isRemoveAction= */ false,
/* data= */ null,
new StreamKey(0, 0, 0),
new StreamKey(1, 1, 1));
DashDownloadAction action13 =
newAction(
uri1,
/* isRemoveAction= */ false,
/* data= */ null,
new StreamKey(1, 1, 1),
new StreamKey(0, 0, 0));
DownloadAction action12 =
createDownloadAction(uri1, new StreamKey(0, 0, 0), new StreamKey(1, 1, 1));
DownloadAction action13 =
createDownloadAction(uri1, new StreamKey(1, 1, 1), new StreamKey(0, 0, 0));
assertEqual(action12, action13);
DashDownloadAction action14 =
newAction(uri1, /* isRemoveAction= */ false, /* data= */ null, new StreamKey(0, 0, 0));
DashDownloadAction action15 =
newAction(
uri1,
/* isRemoveAction= */ false,
/* data= */ null,
new StreamKey(1, 1, 1),
new StreamKey(0, 0, 0));
DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0, 0));
DownloadAction action15 =
createDownloadAction(uri1, new StreamKey(1, 1, 1), new StreamKey(0, 0, 0));
assertNotEqual(action14, action15);
DashDownloadAction action16 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DashDownloadAction action17 = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DownloadAction action16 = createDownloadAction(uri1);
DownloadAction action17 = createDownloadAction(uri1);
assertEqual(action16, action17);
}
@Test
public void testSerializerGetType() {
DashDownloadAction action = newAction(uri1, /* isRemoveAction= */ false, /* data= */ null);
DownloadAction action = createDownloadAction(uri1);
assertThat(action.type).isNotNull();
}
@Test
public void testSerializerWriteRead() throws Exception {
doTestSerializationRoundTrip(newAction(uri1, /* isRemoveAction= */ false, /* data= */ null));
doTestSerializationRoundTrip(newAction(uri1, /* isRemoveAction= */ true, /* data= */ null));
doTestSerializationRoundTrip(createDownloadAction(uri1));
doTestSerializationRoundTrip(createRemoveAction(uri1));
doTestSerializationRoundTrip(
newAction(
uri2,
/* isRemoveAction= */ false,
/* data= */ null,
new StreamKey(0, 0, 0),
new StreamKey(1, 1, 1)));
createDownloadAction(uri2, new StreamKey(0, 0, 0), new StreamKey(1, 1, 1)));
}
private static void assertNotEqual(DashDownloadAction action1, DashDownloadAction action2) {
private static void assertNotEqual(DownloadAction action1, DownloadAction action2) {
assertThat(action1).isNotEqualTo(action2);
assertThat(action2).isNotEqualTo(action1);
}
private static void assertEqual(DashDownloadAction action1, DashDownloadAction action2) {
private static void assertEqual(DownloadAction action1, DownloadAction action2) {
assertThat(action1).isEqualTo(action2);
assertThat(action2).isEqualTo(action1);
}
private static void doTestSerializationRoundTrip(DashDownloadAction action) throws IOException {
private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(out);
DownloadAction.serializeToStream(action, output);
@ -192,10 +165,13 @@ public class DashDownloadActionTest {
assertThat(action).isEqualTo(action2);
}
private static DashDownloadAction newAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) {
private static DownloadAction createDownloadAction(Uri uri, StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
return new DashDownloadAction(uri, isRemoveAction, data, keysList);
return DashDownloadAction.createDownloadAction(uri, null, keysList);
}
private static DownloadAction createRemoveAction(Uri uri) {
return DashDownloadAction.createRemoveAction(uri, null);
}
}

View File

@ -25,6 +25,7 @@ import android.content.Context;
import android.net.Uri;
import android.os.ConditionVariable;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.offline.DownloadAction;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.StreamKey;
@ -279,10 +280,16 @@ public class DownloadManagerDashTest {
});
}
private static DashDownloadAction newAction(
private static DownloadAction newAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
return new DashDownloadAction(uri, isRemoveAction, data, keysList);
DownloadAction result;
if (isRemoveAction) {
result = DashDownloadAction.createRemoveAction(uri, data);
} else {
result = DashDownloadAction.createDownloadAction(uri, data, keysList);
}
return result;
}
}

View File

@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.offline.DownloadAction;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
import com.google.android.exoplayer2.offline.DownloadService;
@ -216,7 +217,7 @@ public class DownloadServiceDashTest {
callDownloadServiceOnStart(newAction(TEST_MPD_URI, false, null, keys));
}
private void callDownloadServiceOnStart(final DashDownloadAction action) {
private void callDownloadServiceOnStart(final DownloadAction action) {
dummyMainThread.runOnMainThread(
new Runnable() {
@Override
@ -228,10 +229,16 @@ public class DownloadServiceDashTest {
});
}
private static DashDownloadAction newAction(
private static DownloadAction newAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
return new DashDownloadAction(uri, isRemoveAction, data, keysList);
DownloadAction result;
if (isRemoveAction) {
result = DashDownloadAction.createRemoveAction(uri, data);
} else {
result = DashDownloadAction.createDownloadAction(uri, data, keysList);
}
return result;
}
}

View File

@ -26,6 +26,7 @@ android {
defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
consumerProguardFiles 'proguard-rules.txt'
}
buildTypes {

View File

@ -0,0 +1,7 @@
# Proguard rules specific to the hls module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction {
static ** DESERIALIZER;
}

View File

@ -22,21 +22,24 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.SegmentDownloadAction;
import com.google.android.exoplayer2.offline.StreamKey;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/** An action to download or remove downloaded HLS streams. */
public final class HlsDownloadAction extends SegmentDownloadAction {
private static final String TYPE = "hls";
private static final int VERSION = 0;
private static final int VERSION = 1;
public static final Deserializer DESERIALIZER =
new SegmentDownloadActionDeserializer(TYPE, VERSION) {
@Override
protected StreamKey readKey(DataInputStream input) throws IOException {
protected StreamKey readKey(int version, DataInputStream input) throws IOException {
if (version > 0) {
return super.readKey(version, input);
}
int renditionGroup = input.readInt();
int trackIndex = input.readInt();
return new StreamKey(renditionGroup, trackIndex);
@ -49,27 +52,46 @@ public final class HlsDownloadAction extends SegmentDownloadAction {
}
};
/**
* Creates a HLS download action.
*
* @param uri The URI of the media to be downloaded.
* @param data Optional custom data for this action. If {@code null} an empty array will be used.
* @param keys Keys of tracks to be downloaded. If empty, all tracks will be downloaded.
*/
public static HlsDownloadAction createDownloadAction(
Uri uri, @Nullable byte[] data, List<StreamKey> keys) {
return new HlsDownloadAction(uri, /* isRemoveAction= */ false, data, keys);
}
/**
* Creates a HLS remove action.
*
* @param uri The URI of the media to be removed.
* @param data Optional custom data for this action. If {@code null} an empty array will be used.
*/
public static HlsDownloadAction createRemoveAction(Uri uri, @Nullable byte[] data) {
return new HlsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList());
}
/**
* @param uri The HLS playlist URI.
* @param isRemoveAction Whether the data will be removed. If {@code false} it will be downloaded.
* @param data Optional custom data for this action.
* @param keys Keys of renditions to be downloaded. If empty, all renditions are downloaded. If
* {@code removeAction} is true, {@code keys} must empty.
* @deprecated Use {@link #createDownloadAction(Uri, byte[], List)} or {@link
* #createRemoveAction(Uri, byte[])}.
*/
@Deprecated
public HlsDownloadAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, List<StreamKey> keys) {
super(TYPE, VERSION, uri, isRemoveAction, data, keys);
}
@Override
protected HlsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
public HlsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
return new HlsDownloader(uri, keys, constructorHelper);
}
@Override
protected void writeKey(DataOutputStream output, StreamKey key) throws IOException {
output.writeInt(key.groupIndex);
output.writeInt(key.trackIndex);
}
}

View File

@ -34,7 +34,6 @@ 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;
@ -100,13 +99,13 @@ public final class HlsDownloadHelper extends DownloadHelper {
@Override
public HlsDownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
Assertions.checkNotNull(renditionGroups);
return new HlsDownloadAction(
uri, /* isRemoveAction= */ false, data, toStreamKeys(trackKeys, renditionGroups));
return HlsDownloadAction.createDownloadAction(
uri, data, toStreamKeys(trackKeys, renditionGroups));
}
@Override
public HlsDownloadAction getRemoveAction(@Nullable byte[] data) {
return new HlsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList());
return HlsDownloadAction.createRemoveAction(uri, data);
}
private static Format[] toFormats(List<HlsMasterPlaylist.HlsUrl> hlsUrls) {

View File

@ -0,0 +1,7 @@
# Proguard rules specific to the hls module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction {
static ** DESERIALIZER;
}

View File

@ -0,0 +1,205 @@
/*
* 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 static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import com.google.android.exoplayer2.offline.DownloadAction;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.cache.Cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
/** Unit tests for {@link HlsDownloadAction}. */
@RunWith(RobolectricTestRunner.class)
public class HlsDownloadActionTest {
private Uri uri1;
private Uri uri2;
@Before
public void setUp() {
uri1 = Uri.parse("http://test1.uri");
uri2 = Uri.parse("http://test2.uri");
}
@Test
public void testDownloadActionIsNotRemoveAction() {
DownloadAction action = createDownloadAction(uri1);
assertThat(action.isRemoveAction).isFalse();
}
@Test
public void testRemoveActionIsRemoveAction() {
DownloadAction action2 = createRemoveAction(uri1);
assertThat(action2.isRemoveAction).isTrue();
}
@Test
public void testCreateDownloader() {
MockitoAnnotations.initMocks(this);
DownloadAction action = createDownloadAction(uri1);
DownloaderConstructorHelper constructorHelper =
new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY);
assertThat(action.createDownloader(constructorHelper)).isNotNull();
}
@Test
public void testSameUriDifferentAction_IsSameMedia() {
DownloadAction action1 = createRemoveAction(uri1);
DownloadAction action2 = createDownloadAction(uri1);
assertThat(action1.isSameMedia(action2)).isTrue();
}
@Test
public void testDifferentUriAndAction_IsNotSameMedia() {
DownloadAction action3 = createRemoveAction(uri2);
DownloadAction action4 = createDownloadAction(uri1);
assertThat(action3.isSameMedia(action4)).isFalse();
}
@SuppressWarnings("EqualsWithItself")
@Test
public void testEquals() {
DownloadAction action1 = createRemoveAction(uri1);
assertThat(action1.equals(action1)).isTrue();
DownloadAction action2 = createRemoveAction(uri1);
DownloadAction action3 = createRemoveAction(uri1);
assertEqual(action2, action3);
DownloadAction action4 = createRemoveAction(uri1);
DownloadAction action5 = createDownloadAction(uri1);
assertNotEqual(action4, action5);
DownloadAction action6 = createDownloadAction(uri1);
DownloadAction action7 = createDownloadAction(uri1, new StreamKey(0, 0));
assertNotEqual(action6, action7);
DownloadAction action8 = createDownloadAction(uri1, new StreamKey(1, 1));
DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0));
assertNotEqual(action8, action9);
DownloadAction action10 = createRemoveAction(uri1);
DownloadAction action11 = createRemoveAction(uri2);
assertNotEqual(action10, action11);
DownloadAction action12 = createDownloadAction(uri1, new StreamKey(0, 0), new StreamKey(1, 1));
DownloadAction action13 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0));
assertEqual(action12, action13);
DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0));
DownloadAction action15 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0));
assertNotEqual(action14, action15);
DownloadAction action16 = createDownloadAction(uri1);
DownloadAction action17 = createDownloadAction(uri1);
assertEqual(action16, action17);
}
@Test
public void testSerializerGetType() {
DownloadAction action = createDownloadAction(uri1);
assertThat(action.type).isNotNull();
}
@Test
public void testSerializerWriteRead() throws Exception {
doTestSerializationRoundTrip(createDownloadAction(uri1));
doTestSerializationRoundTrip(createRemoveAction(uri1));
doTestSerializationRoundTrip(
createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1)));
}
@Test
public void testSerializerVersion0() throws Exception {
doTestSerializationV0RoundTrip(createDownloadAction(uri1));
doTestSerializationV0RoundTrip(createRemoveAction(uri1));
doTestSerializationV0RoundTrip(
createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1)));
}
private static void assertNotEqual(DownloadAction action1, DownloadAction action2) {
assertThat(action1).isNotEqualTo(action2);
assertThat(action2).isNotEqualTo(action1);
}
private static void assertEqual(DownloadAction action1, DownloadAction action2) {
assertThat(action1).isEqualTo(action2);
assertThat(action2).isEqualTo(action1);
}
private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(out);
DownloadAction.serializeToStream(action, output);
assertEqual(action, deserializeActionFromStream(out));
}
private static void doTestSerializationV0RoundTrip(HlsDownloadAction action) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(out);
DataOutputStream dataOutputStream = new DataOutputStream(output);
dataOutputStream.writeUTF(action.type);
dataOutputStream.writeInt(/* version */ 0);
dataOutputStream.writeUTF(action.uri.toString());
dataOutputStream.writeBoolean(action.isRemoveAction);
dataOutputStream.writeInt(action.data.length);
dataOutputStream.write(action.data);
dataOutputStream.writeInt(action.keys.size());
for (int i = 0; i < action.keys.size(); i++) {
StreamKey key = action.keys.get(i);
dataOutputStream.writeInt(key.groupIndex);
dataOutputStream.writeInt(key.trackIndex);
}
dataOutputStream.flush();
assertEqual(action, deserializeActionFromStream(out));
}
private static DownloadAction deserializeActionFromStream(ByteArrayOutputStream out)
throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
DataInputStream input = new DataInputStream(in);
return DownloadAction.deserializeFromStream(
new DownloadAction.Deserializer[] {HlsDownloadAction.DESERIALIZER}, input);
}
private static HlsDownloadAction createDownloadAction(Uri uri, StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
return HlsDownloadAction.createDownloadAction(uri, null, keysList);
}
private static HlsDownloadAction createRemoveAction(Uri uri) {
return HlsDownloadAction.createRemoveAction(uri, null);
}
}

View File

@ -26,6 +26,7 @@ android {
defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
consumerProguardFiles 'proguard-rules.txt'
}
buildTypes {

View File

@ -0,0 +1,7 @@
# Proguard rules specific to the smoothstreaming module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction {
static ** DESERIALIZER;
}

View File

@ -22,21 +22,24 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.SegmentDownloadAction;
import com.google.android.exoplayer2.offline.StreamKey;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/** An action to download or remove downloaded SmoothStreaming streams. */
public final class SsDownloadAction extends SegmentDownloadAction {
private static final String TYPE = "ss";
private static final int VERSION = 0;
private static final int VERSION = 1;
public static final Deserializer DESERIALIZER =
new SegmentDownloadActionDeserializer(TYPE, VERSION) {
@Override
protected StreamKey readKey(DataInputStream input) throws IOException {
protected StreamKey readKey(int version, DataInputStream input) throws IOException {
if (version > 0) {
return super.readKey(version, input);
}
int groupIndex = input.readInt();
int trackIndex = input.readInt();
return new StreamKey(groupIndex, trackIndex);
@ -49,27 +52,46 @@ public final class SsDownloadAction extends SegmentDownloadAction {
}
};
/**
* Creates a SmoothStreaming download action.
*
* @param uri The URI of the media to be downloaded.
* @param data Optional custom data for this action. If {@code null} an empty array will be used.
* @param keys Keys of tracks to be downloaded. If empty, all tracks will be downloaded.
*/
public static SsDownloadAction createDownloadAction(
Uri uri, @Nullable byte[] data, List<StreamKey> keys) {
return new SsDownloadAction(uri, /* isRemoveAction= */ false, data, keys);
}
/**
* Creates a SmoothStreaming remove action.
*
* @param uri The URI of the media to be removed.
* @param data Optional custom data for this action. If {@code null} an empty array will be used.
*/
public static SsDownloadAction createRemoveAction(Uri uri, @Nullable byte[] data) {
return new SsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList());
}
/**
* @param uri The SmoothStreaming manifest URI.
* @param isRemoveAction Whether the data will be removed. If {@code false} it will be downloaded.
* @param data Optional custom data for this action.
* @param keys Keys of streams to be downloaded. If empty, all streams are downloaded. If {@code
* removeAction} is true, {@code keys} must be empty.
* @deprecated Use {@link #createDownloadAction(Uri, byte[], List)} or {@link
* #createRemoveAction(Uri, byte[])}.
*/
@Deprecated
public SsDownloadAction(
Uri uri, boolean isRemoveAction, @Nullable byte[] data, List<StreamKey> keys) {
super(TYPE, VERSION, uri, isRemoveAction, data, keys);
}
@Override
protected SsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
public SsDownloader createDownloader(DownloaderConstructorHelper constructorHelper) {
return new SsDownloader(uri, keys, constructorHelper);
}
@Override
protected void writeKey(DataOutputStream output, StreamKey key) throws IOException {
output.writeInt(key.groupIndex);
output.writeInt(key.trackIndex);
}
}

View File

@ -30,7 +30,6 @@ 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;
@ -78,12 +77,12 @@ public final class SsDownloadHelper extends DownloadHelper {
@Override
public SsDownloadAction getDownloadAction(@Nullable byte[] data, List<TrackKey> trackKeys) {
return new SsDownloadAction(uri, /* isRemoveAction= */ false, data, toStreamKeys(trackKeys));
return SsDownloadAction.createDownloadAction(uri, data, toStreamKeys(trackKeys));
}
@Override
public SsDownloadAction getRemoveAction(@Nullable byte[] data) {
return new SsDownloadAction(uri, /* isRemoveAction= */ true, data, Collections.emptyList());
return SsDownloadAction.createRemoveAction(uri, data);
}
private static List<StreamKey> toStreamKeys(List<TrackKey> trackKeys) {

View File

@ -0,0 +1,7 @@
# Proguard rules specific to the smoothstreaming module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction {
static ** DESERIALIZER;
}

View File

@ -0,0 +1,205 @@
/*
* 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 static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import com.google.android.exoplayer2.offline.DownloadAction;
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.cache.Cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
/** Unit tests for {@link SsDownloadAction}. */
@RunWith(RobolectricTestRunner.class)
public class SsDownloadActionTest {
private Uri uri1;
private Uri uri2;
@Before
public void setUp() {
uri1 = Uri.parse("http://test/1.uri");
uri2 = Uri.parse("http://test/2.uri");
}
@Test
public void testDownloadActionIsNotRemoveAction() {
DownloadAction action = createDownloadAction(uri1);
assertThat(action.isRemoveAction).isFalse();
}
@Test
public void testRemoveActionIsRemoveAction() {
DownloadAction action2 = createRemoveAction(uri1);
assertThat(action2.isRemoveAction).isTrue();
}
@Test
public void testCreateDownloader() {
MockitoAnnotations.initMocks(this);
DownloadAction action = createDownloadAction(uri1);
DownloaderConstructorHelper constructorHelper =
new DownloaderConstructorHelper(Mockito.mock(Cache.class), DummyDataSource.FACTORY);
assertThat(action.createDownloader(constructorHelper)).isNotNull();
}
@Test
public void testSameUriDifferentAction_IsSameMedia() {
DownloadAction action1 = createRemoveAction(uri1);
DownloadAction action2 = createDownloadAction(uri1);
assertThat(action1.isSameMedia(action2)).isTrue();
}
@Test
public void testDifferentUriAndAction_IsNotSameMedia() {
DownloadAction action3 = createRemoveAction(uri2);
DownloadAction action4 = createDownloadAction(uri1);
assertThat(action3.isSameMedia(action4)).isFalse();
}
@SuppressWarnings("EqualsWithItself")
@Test
public void testEquals() {
DownloadAction action1 = createRemoveAction(uri1);
assertThat(action1.equals(action1)).isTrue();
DownloadAction action2 = createRemoveAction(uri1);
DownloadAction action3 = createRemoveAction(uri1);
assertEqual(action2, action3);
DownloadAction action4 = createRemoveAction(uri1);
DownloadAction action5 = createDownloadAction(uri1);
assertNotEqual(action4, action5);
DownloadAction action6 = createDownloadAction(uri1);
DownloadAction action7 = createDownloadAction(uri1, new StreamKey(0, 0));
assertNotEqual(action6, action7);
DownloadAction action8 = createDownloadAction(uri1, new StreamKey(1, 1));
DownloadAction action9 = createDownloadAction(uri1, new StreamKey(0, 0));
assertNotEqual(action8, action9);
DownloadAction action10 = createRemoveAction(uri1);
DownloadAction action11 = createRemoveAction(uri2);
assertNotEqual(action10, action11);
DownloadAction action12 = createDownloadAction(uri1, new StreamKey(0, 0), new StreamKey(1, 1));
DownloadAction action13 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0));
assertEqual(action12, action13);
DownloadAction action14 = createDownloadAction(uri1, new StreamKey(0, 0));
DownloadAction action15 = createDownloadAction(uri1, new StreamKey(1, 1), new StreamKey(0, 0));
assertNotEqual(action14, action15);
DownloadAction action16 = createDownloadAction(uri1);
DownloadAction action17 = createDownloadAction(uri1);
assertEqual(action16, action17);
}
@Test
public void testSerializerGetType() {
DownloadAction action = createDownloadAction(uri1);
assertThat(action.type).isNotNull();
}
@Test
public void testSerializerWriteRead() throws Exception {
doTestSerializationRoundTrip(createDownloadAction(uri1));
doTestSerializationRoundTrip(createRemoveAction(uri1));
doTestSerializationRoundTrip(
createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1)));
}
@Test
public void testSerializerVersion0() throws Exception {
doTestSerializationV0RoundTrip(createDownloadAction(uri1));
doTestSerializationV0RoundTrip(createRemoveAction(uri1));
doTestSerializationV0RoundTrip(
createDownloadAction(uri2, new StreamKey(0, 0), new StreamKey(1, 1)));
}
private static void assertNotEqual(DownloadAction action1, DownloadAction action2) {
assertThat(action1).isNotEqualTo(action2);
assertThat(action2).isNotEqualTo(action1);
}
private static void assertEqual(DownloadAction action1, DownloadAction action2) {
assertThat(action1).isEqualTo(action2);
assertThat(action2).isEqualTo(action1);
}
private static void doTestSerializationRoundTrip(DownloadAction action) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(out);
DownloadAction.serializeToStream(action, output);
assertEqual(action, deserializeActionFromStream(out));
}
private static void doTestSerializationV0RoundTrip(SsDownloadAction action) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(out);
DataOutputStream dataOutputStream = new DataOutputStream(output);
dataOutputStream.writeUTF(action.type);
dataOutputStream.writeInt(/* version */ 0);
dataOutputStream.writeUTF(action.uri.toString());
dataOutputStream.writeBoolean(action.isRemoveAction);
dataOutputStream.writeInt(action.data.length);
dataOutputStream.write(action.data);
dataOutputStream.writeInt(action.keys.size());
for (int i = 0; i < action.keys.size(); i++) {
StreamKey key = action.keys.get(i);
dataOutputStream.writeInt(key.groupIndex);
dataOutputStream.writeInt(key.trackIndex);
}
dataOutputStream.flush();
assertEqual(action, deserializeActionFromStream(out));
}
private static DownloadAction deserializeActionFromStream(ByteArrayOutputStream out)
throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
DataInputStream input = new DataInputStream(in);
return DownloadAction.deserializeFromStream(
new DownloadAction.Deserializer[] {SsDownloadAction.DESERIALIZER}, input);
}
private static SsDownloadAction createDownloadAction(Uri uri, StreamKey... keys) {
ArrayList<StreamKey> keysList = new ArrayList<>();
Collections.addAll(keysList, keys);
return SsDownloadAction.createDownloadAction(uri, null, keysList);
}
private static SsDownloadAction createRemoveAction(Uri uri) {
return SsDownloadAction.createRemoveAction(uri, null);
}
}