mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Offline: store MIME type and keySetId
Replace `type` with (optional) `mimeType` and add `keySetId` in DownloadRequest. The DownloadHelper infers the downloading method (DASH, HLS, SmoothStreaming or Progressive) from the content's MIME type and URI. PiperOrigin-RevId: 322117384
This commit is contained in:
parent
c669756f7d
commit
b249480060
23 changed files with 534 additions and 309 deletions
|
|
@ -30,6 +30,8 @@ import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.database.DatabaseUtils;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.media.AudioFormat;
|
import android.media.AudioFormat;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
|
@ -2227,6 +2229,14 @@ public final class Util {
|
||||||
items.addAll(Math.min(newFromIndex, items.size()), removedItems);
|
items.addAll(Math.min(newFromIndex, items.size()), removedItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether the table exists in the database. */
|
||||||
|
public static boolean tableExists(SQLiteDatabase database, String tableName) {
|
||||||
|
long count =
|
||||||
|
DatabaseUtils.queryNumEntries(
|
||||||
|
database, "sqlite_master", "tbl_name = ?", new String[] {tableName});
|
||||||
|
return count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static String getSystemProperty(String name) {
|
private static String getSystemProperty(String name) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.util;
|
package com.google.android.exoplayer2.util;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.testutil.TestUtil.getInMemoryDatabaseProvider;
|
||||||
import static com.google.android.exoplayer2.util.Util.binarySearchCeil;
|
import static com.google.android.exoplayer2.util.Util.binarySearchCeil;
|
||||||
import static com.google.android.exoplayer2.util.Util.binarySearchFloor;
|
import static com.google.android.exoplayer2.util.Util.binarySearchFloor;
|
||||||
import static com.google.android.exoplayer2.util.Util.escapeFileName;
|
import static com.google.android.exoplayer2.util.Util.escapeFileName;
|
||||||
|
|
@ -24,6 +25,7 @@ import static com.google.android.exoplayer2.util.Util.parseXsDuration;
|
||||||
import static com.google.android.exoplayer2.util.Util.unescapeFileName;
|
import static com.google.android.exoplayer2.util.Util.unescapeFileName;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.style.StrikethroughSpan;
|
import android.text.style.StrikethroughSpan;
|
||||||
|
|
@ -997,6 +999,21 @@ public class UtilTest {
|
||||||
assertThat(Util.normalizeLanguageCode("hsn")).isEqualTo("zh-hsn");
|
assertThat(Util.normalizeLanguageCode("hsn")).isEqualTo("zh-hsn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tableExists_withExistingTable() {
|
||||||
|
SQLiteDatabase database = getInMemoryDatabaseProvider().getWritableDatabase();
|
||||||
|
database.execSQL("CREATE TABLE TestTable (ID INTEGER NOT NULL)");
|
||||||
|
|
||||||
|
assertThat(Util.tableExists(database, "TestTable")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tableExists_withNonExistingTable() {
|
||||||
|
SQLiteDatabase database = getInMemoryDatabaseProvider().getReadableDatabase();
|
||||||
|
|
||||||
|
assertThat(Util.tableExists(database, "table")).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertEscapeUnescapeFileName(String fileName, String escapedFileName) {
|
private static void assertEscapeUnescapeFileName(String fileName, String escapedFileName) {
|
||||||
assertThat(escapeFileName(fileName)).isEqualTo(escapedFileName);
|
assertThat(escapeFileName(fileName)).isEqualTo(escapedFileName);
|
||||||
assertThat(unescapeFileName(escapedFileName)).isEqualTo(fileName);
|
assertThat(unescapeFileName(escapedFileName)).isEqualTo(fileName);
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@ package com.google.android.exoplayer2.database;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.database.SQLException;
|
import android.database.SQLException;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
@ -115,7 +114,7 @@ public final class VersionTable {
|
||||||
SQLiteDatabase writableDatabase, @Feature int feature, String instanceUid)
|
SQLiteDatabase writableDatabase, @Feature int feature, String instanceUid)
|
||||||
throws DatabaseIOException {
|
throws DatabaseIOException {
|
||||||
try {
|
try {
|
||||||
if (!tableExists(writableDatabase, TABLE_NAME)) {
|
if (!Util.tableExists(writableDatabase, TABLE_NAME)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
writableDatabase.delete(
|
writableDatabase.delete(
|
||||||
|
|
@ -140,7 +139,7 @@ public final class VersionTable {
|
||||||
public static int getVersion(SQLiteDatabase database, @Feature int feature, String instanceUid)
|
public static int getVersion(SQLiteDatabase database, @Feature int feature, String instanceUid)
|
||||||
throws DatabaseIOException {
|
throws DatabaseIOException {
|
||||||
try {
|
try {
|
||||||
if (!tableExists(database, TABLE_NAME)) {
|
if (!Util.tableExists(database, TABLE_NAME)) {
|
||||||
return VERSION_UNSET;
|
return VERSION_UNSET;
|
||||||
}
|
}
|
||||||
try (Cursor cursor =
|
try (Cursor cursor =
|
||||||
|
|
@ -163,14 +162,6 @@ public final class VersionTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
/* package */ static boolean tableExists(SQLiteDatabase readableDatabase, String tableName) {
|
|
||||||
long count =
|
|
||||||
DatabaseUtils.queryNumEntries(
|
|
||||||
readableDatabase, "sqlite_master", "tbl_name = ?", new String[] {tableName});
|
|
||||||
return count > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] featureAndInstanceUidArguments(int feature, String instance) {
|
private static String[] featureAndInstanceUidArguments(int feature, String instance) {
|
||||||
return new String[] {Integer.toString(feature), instance};
|
return new String[] {Integer.toString(feature), instance};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import android.net.Uri;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.offline.DownloadRequest.UnsupportedRequestException;
|
import com.google.android.exoplayer2.offline.DownloadRequest.UnsupportedRequestException;
|
||||||
import com.google.android.exoplayer2.util.AtomicFile;
|
import com.google.android.exoplayer2.util.AtomicFile;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
@ -37,6 +38,10 @@ import java.util.List;
|
||||||
/* package */ final class ActionFile {
|
/* package */ final class ActionFile {
|
||||||
|
|
||||||
private static final int VERSION = 0;
|
private static final int VERSION = 0;
|
||||||
|
private static final String DOWNLOAD_TYPE_PROGRESSIVE = "progressive";
|
||||||
|
private static final String DOWNLOAD_TYPE_DASH = "dash";
|
||||||
|
private static final String DOWNLOAD_TYPE_HLS = "hls";
|
||||||
|
private static final String DOWNLOAD_TYPE_SS = "ss";
|
||||||
|
|
||||||
private final AtomicFile atomicFile;
|
private final AtomicFile atomicFile;
|
||||||
|
|
||||||
|
|
@ -92,7 +97,7 @@ import java.util.List;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DownloadRequest readDownloadRequest(DataInputStream input) throws IOException {
|
private static DownloadRequest readDownloadRequest(DataInputStream input) throws IOException {
|
||||||
String type = input.readUTF();
|
String downloadType = input.readUTF();
|
||||||
int version = input.readInt();
|
int version = input.readInt();
|
||||||
|
|
||||||
Uri uri = Uri.parse(input.readUTF());
|
Uri uri = Uri.parse(input.readUTF());
|
||||||
|
|
@ -108,21 +113,21 @@ import java.util.List;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialized version 0 progressive actions did not contain keys.
|
// Serialized version 0 progressive actions did not contain keys.
|
||||||
boolean isLegacyProgressive = version == 0 && DownloadRequest.TYPE_PROGRESSIVE.equals(type);
|
boolean isLegacyProgressive = version == 0 && DOWNLOAD_TYPE_PROGRESSIVE.equals(downloadType);
|
||||||
List<StreamKey> keys = new ArrayList<>();
|
List<StreamKey> keys = new ArrayList<>();
|
||||||
if (!isLegacyProgressive) {
|
if (!isLegacyProgressive) {
|
||||||
int keyCount = input.readInt();
|
int keyCount = input.readInt();
|
||||||
for (int i = 0; i < keyCount; i++) {
|
for (int i = 0; i < keyCount; i++) {
|
||||||
keys.add(readKey(type, version, input));
|
keys.add(readKey(downloadType, version, input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key.
|
// Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key.
|
||||||
boolean isLegacySegmented =
|
boolean isLegacySegmented =
|
||||||
version < 2
|
version < 2
|
||||||
&& (DownloadRequest.TYPE_DASH.equals(type)
|
&& (DOWNLOAD_TYPE_DASH.equals(downloadType)
|
||||||
|| DownloadRequest.TYPE_HLS.equals(type)
|
|| DOWNLOAD_TYPE_HLS.equals(downloadType)
|
||||||
|| DownloadRequest.TYPE_SS.equals(type));
|
|| DOWNLOAD_TYPE_SS.equals(downloadType));
|
||||||
@Nullable String customCacheKey = null;
|
@Nullable String customCacheKey = null;
|
||||||
if (!isLegacySegmented) {
|
if (!isLegacySegmented) {
|
||||||
customCacheKey = input.readBoolean() ? input.readUTF() : null;
|
customCacheKey = input.readBoolean() ? input.readUTF() : null;
|
||||||
|
|
@ -135,7 +140,10 @@ import java.util.List;
|
||||||
// Remove actions are not supported anymore.
|
// Remove actions are not supported anymore.
|
||||||
throw new UnsupportedRequestException();
|
throw new UnsupportedRequestException();
|
||||||
}
|
}
|
||||||
return new DownloadRequest(id, type, uri, keys, customCacheKey, data);
|
// keySetId and mimeType were not supported. Set keySetId to null and try to infer the mime
|
||||||
|
// type from the download type.
|
||||||
|
return new DownloadRequest(
|
||||||
|
id, uri, inferMimeType(downloadType), keys, /* keySetId= */ null, customCacheKey, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StreamKey readKey(String type, int version, DataInputStream input)
|
private static StreamKey readKey(String type, int version, DataInputStream input)
|
||||||
|
|
@ -145,8 +153,7 @@ import java.util.List;
|
||||||
int trackIndex;
|
int trackIndex;
|
||||||
|
|
||||||
// Serialized version 0 HLS/SS actions did not contain a period index.
|
// Serialized version 0 HLS/SS actions did not contain a period index.
|
||||||
if ((DownloadRequest.TYPE_HLS.equals(type) || DownloadRequest.TYPE_SS.equals(type))
|
if ((DOWNLOAD_TYPE_HLS.equals(type) || DOWNLOAD_TYPE_SS.equals(type)) && version == 0) {
|
||||||
&& version == 0) {
|
|
||||||
periodIndex = 0;
|
periodIndex = 0;
|
||||||
groupIndex = input.readInt();
|
groupIndex = input.readInt();
|
||||||
trackIndex = input.readInt();
|
trackIndex = input.readInt();
|
||||||
|
|
@ -158,6 +165,20 @@ import java.util.List;
|
||||||
return new StreamKey(periodIndex, groupIndex, trackIndex);
|
return new StreamKey(periodIndex, groupIndex, trackIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String inferMimeType(String downloadType) {
|
||||||
|
switch (downloadType) {
|
||||||
|
case DOWNLOAD_TYPE_DASH:
|
||||||
|
return MimeTypes.APPLICATION_MPD;
|
||||||
|
case DOWNLOAD_TYPE_HLS:
|
||||||
|
return MimeTypes.APPLICATION_M3U8;
|
||||||
|
case DOWNLOAD_TYPE_SS:
|
||||||
|
return MimeTypes.APPLICATION_SS;
|
||||||
|
case DOWNLOAD_TYPE_PROGRESSIVE:
|
||||||
|
default:
|
||||||
|
return MimeTypes.VIDEO_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String generateDownloadId(Uri uri, @Nullable String customCacheKey) {
|
private static String generateDownloadId(Uri uri, @Nullable String customCacheKey) {
|
||||||
return customCacheKey != null ? customCacheKey : uri.toString();
|
return customCacheKey != null ? customCacheKey : uri.toString();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import com.google.android.exoplayer2.database.VersionTable;
|
||||||
import com.google.android.exoplayer2.offline.Download.FailureReason;
|
import com.google.android.exoplayer2.offline.Download.FailureReason;
|
||||||
import com.google.android.exoplayer2.offline.Download.State;
|
import com.google.android.exoplayer2.offline.Download.State;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -38,10 +39,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
|
|
||||||
private static final String TABLE_PREFIX = DatabaseProvider.TABLE_PREFIX + "Downloads";
|
private static final String TABLE_PREFIX = DatabaseProvider.TABLE_PREFIX + "Downloads";
|
||||||
|
|
||||||
@VisibleForTesting /* package */ static final int TABLE_VERSION = 2;
|
@VisibleForTesting /* package */ static final int TABLE_VERSION = 3;
|
||||||
|
|
||||||
private static final String COLUMN_ID = "id";
|
private static final String COLUMN_ID = "id";
|
||||||
private static final String COLUMN_TYPE = "title";
|
private static final String COLUMN_MIME_TYPE = "mime_type";
|
||||||
private static final String COLUMN_URI = "uri";
|
private static final String COLUMN_URI = "uri";
|
||||||
private static final String COLUMN_STREAM_KEYS = "stream_keys";
|
private static final String COLUMN_STREAM_KEYS = "stream_keys";
|
||||||
private static final String COLUMN_CUSTOM_CACHE_KEY = "custom_cache_key";
|
private static final String COLUMN_CUSTOM_CACHE_KEY = "custom_cache_key";
|
||||||
|
|
@ -54,9 +55,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
private static final String COLUMN_FAILURE_REASON = "failure_reason";
|
private static final String COLUMN_FAILURE_REASON = "failure_reason";
|
||||||
private static final String COLUMN_PERCENT_DOWNLOADED = "percent_downloaded";
|
private static final String COLUMN_PERCENT_DOWNLOADED = "percent_downloaded";
|
||||||
private static final String COLUMN_BYTES_DOWNLOADED = "bytes_downloaded";
|
private static final String COLUMN_BYTES_DOWNLOADED = "bytes_downloaded";
|
||||||
|
private static final String COLUMN_KEY_SET_ID = "key_set_id";
|
||||||
|
|
||||||
private static final int COLUMN_INDEX_ID = 0;
|
private static final int COLUMN_INDEX_ID = 0;
|
||||||
private static final int COLUMN_INDEX_TYPE = 1;
|
private static final int COLUMN_INDEX_MIME_TYPE = 1;
|
||||||
private static final int COLUMN_INDEX_URI = 2;
|
private static final int COLUMN_INDEX_URI = 2;
|
||||||
private static final int COLUMN_INDEX_STREAM_KEYS = 3;
|
private static final int COLUMN_INDEX_STREAM_KEYS = 3;
|
||||||
private static final int COLUMN_INDEX_CUSTOM_CACHE_KEY = 4;
|
private static final int COLUMN_INDEX_CUSTOM_CACHE_KEY = 4;
|
||||||
|
|
@ -69,6 +71,7 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
private static final int COLUMN_INDEX_FAILURE_REASON = 11;
|
private static final int COLUMN_INDEX_FAILURE_REASON = 11;
|
||||||
private static final int COLUMN_INDEX_PERCENT_DOWNLOADED = 12;
|
private static final int COLUMN_INDEX_PERCENT_DOWNLOADED = 12;
|
||||||
private static final int COLUMN_INDEX_BYTES_DOWNLOADED = 13;
|
private static final int COLUMN_INDEX_BYTES_DOWNLOADED = 13;
|
||||||
|
private static final int COLUMN_INDEX_KEY_SET_ID = 14;
|
||||||
|
|
||||||
private static final String WHERE_ID_EQUALS = COLUMN_ID + " = ?";
|
private static final String WHERE_ID_EQUALS = COLUMN_ID + " = ?";
|
||||||
private static final String WHERE_STATE_IS_DOWNLOADING =
|
private static final String WHERE_STATE_IS_DOWNLOADING =
|
||||||
|
|
@ -79,7 +82,7 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
private static final String[] COLUMNS =
|
private static final String[] COLUMNS =
|
||||||
new String[] {
|
new String[] {
|
||||||
COLUMN_ID,
|
COLUMN_ID,
|
||||||
COLUMN_TYPE,
|
COLUMN_MIME_TYPE,
|
||||||
COLUMN_URI,
|
COLUMN_URI,
|
||||||
COLUMN_STREAM_KEYS,
|
COLUMN_STREAM_KEYS,
|
||||||
COLUMN_CUSTOM_CACHE_KEY,
|
COLUMN_CUSTOM_CACHE_KEY,
|
||||||
|
|
@ -92,14 +95,15 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
COLUMN_FAILURE_REASON,
|
COLUMN_FAILURE_REASON,
|
||||||
COLUMN_PERCENT_DOWNLOADED,
|
COLUMN_PERCENT_DOWNLOADED,
|
||||||
COLUMN_BYTES_DOWNLOADED,
|
COLUMN_BYTES_DOWNLOADED,
|
||||||
|
COLUMN_KEY_SET_ID
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String TABLE_SCHEMA =
|
private static final String TABLE_SCHEMA =
|
||||||
"("
|
"("
|
||||||
+ COLUMN_ID
|
+ COLUMN_ID
|
||||||
+ " TEXT PRIMARY KEY NOT NULL,"
|
+ " TEXT PRIMARY KEY NOT NULL,"
|
||||||
+ COLUMN_TYPE
|
+ COLUMN_MIME_TYPE
|
||||||
+ " TEXT NOT NULL,"
|
+ " TEXT,"
|
||||||
+ COLUMN_URI
|
+ COLUMN_URI
|
||||||
+ " TEXT NOT NULL,"
|
+ " TEXT NOT NULL,"
|
||||||
+ COLUMN_STREAM_KEYS
|
+ COLUMN_STREAM_KEYS
|
||||||
|
|
@ -123,7 +127,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
+ COLUMN_PERCENT_DOWNLOADED
|
+ COLUMN_PERCENT_DOWNLOADED
|
||||||
+ " REAL NOT NULL,"
|
+ " REAL NOT NULL,"
|
||||||
+ COLUMN_BYTES_DOWNLOADED
|
+ COLUMN_BYTES_DOWNLOADED
|
||||||
+ " INTEGER NOT NULL)";
|
+ " INTEGER NOT NULL,"
|
||||||
|
+ COLUMN_KEY_SET_ID
|
||||||
|
+ " BLOB NOT NULL)";
|
||||||
|
|
||||||
private static final String TRUE = "1";
|
private static final String TRUE = "1";
|
||||||
|
|
||||||
|
|
@ -189,24 +195,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
@Override
|
@Override
|
||||||
public void putDownload(Download download) throws DatabaseIOException {
|
public void putDownload(Download download) throws DatabaseIOException {
|
||||||
ensureInitialized();
|
ensureInitialized();
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(COLUMN_ID, download.request.id);
|
|
||||||
values.put(COLUMN_TYPE, download.request.type);
|
|
||||||
values.put(COLUMN_URI, download.request.uri.toString());
|
|
||||||
values.put(COLUMN_STREAM_KEYS, encodeStreamKeys(download.request.streamKeys));
|
|
||||||
values.put(COLUMN_CUSTOM_CACHE_KEY, download.request.customCacheKey);
|
|
||||||
values.put(COLUMN_DATA, download.request.data);
|
|
||||||
values.put(COLUMN_STATE, download.state);
|
|
||||||
values.put(COLUMN_START_TIME_MS, download.startTimeMs);
|
|
||||||
values.put(COLUMN_UPDATE_TIME_MS, download.updateTimeMs);
|
|
||||||
values.put(COLUMN_CONTENT_LENGTH, download.contentLength);
|
|
||||||
values.put(COLUMN_STOP_REASON, download.stopReason);
|
|
||||||
values.put(COLUMN_FAILURE_REASON, download.failureReason);
|
|
||||||
values.put(COLUMN_PERCENT_DOWNLOADED, download.getPercentDownloaded());
|
|
||||||
values.put(COLUMN_BYTES_DOWNLOADED, download.getBytesDownloaded());
|
|
||||||
try {
|
try {
|
||||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||||
writableDatabase.replaceOrThrow(tableName, /* nullColumnHack= */ null, values);
|
putDownloadInternal(download, writableDatabase);
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteException e) {
|
||||||
throw new DatabaseIOException(e);
|
throw new DatabaseIOException(e);
|
||||||
}
|
}
|
||||||
|
|
@ -294,8 +285,13 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
try {
|
try {
|
||||||
VersionTable.setVersion(
|
VersionTable.setVersion(
|
||||||
writableDatabase, VersionTable.FEATURE_OFFLINE, name, TABLE_VERSION);
|
writableDatabase, VersionTable.FEATURE_OFFLINE, name, TABLE_VERSION);
|
||||||
|
List<Download> upgradedDownloads =
|
||||||
|
version == 2 ? loadDownloadsFromVersion2(writableDatabase) : new ArrayList<>();
|
||||||
writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName);
|
writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName);
|
||||||
writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA);
|
writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA);
|
||||||
|
for (Download download : upgradedDownloads) {
|
||||||
|
putDownloadInternal(download, writableDatabase);
|
||||||
|
}
|
||||||
writableDatabase.setTransactionSuccessful();
|
writableDatabase.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
writableDatabase.endTransaction();
|
writableDatabase.endTransaction();
|
||||||
|
|
@ -307,6 +303,78 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void putDownloadInternal(Download download, SQLiteDatabase database) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_ID, download.request.id);
|
||||||
|
values.put(COLUMN_MIME_TYPE, download.request.mimeType);
|
||||||
|
values.put(COLUMN_URI, download.request.uri.toString());
|
||||||
|
values.put(COLUMN_STREAM_KEYS, encodeStreamKeys(download.request.streamKeys));
|
||||||
|
values.put(COLUMN_CUSTOM_CACHE_KEY, download.request.customCacheKey);
|
||||||
|
values.put(COLUMN_DATA, download.request.data);
|
||||||
|
values.put(COLUMN_STATE, download.state);
|
||||||
|
values.put(COLUMN_START_TIME_MS, download.startTimeMs);
|
||||||
|
values.put(COLUMN_UPDATE_TIME_MS, download.updateTimeMs);
|
||||||
|
values.put(COLUMN_CONTENT_LENGTH, download.contentLength);
|
||||||
|
values.put(COLUMN_STOP_REASON, download.stopReason);
|
||||||
|
values.put(COLUMN_FAILURE_REASON, download.failureReason);
|
||||||
|
values.put(COLUMN_PERCENT_DOWNLOADED, download.getPercentDownloaded());
|
||||||
|
values.put(COLUMN_BYTES_DOWNLOADED, download.getBytesDownloaded());
|
||||||
|
values.put(COLUMN_KEY_SET_ID, download.request.keySetId);
|
||||||
|
database.replaceOrThrow(tableName, /* nullColumnHack= */ null, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Download> loadDownloadsFromVersion2(SQLiteDatabase database) {
|
||||||
|
List<Download> downloads = new ArrayList<>();
|
||||||
|
if (!Util.tableExists(database, tableName)) {
|
||||||
|
return downloads;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] columnsV2 =
|
||||||
|
new String[] {
|
||||||
|
"id",
|
||||||
|
"title",
|
||||||
|
"uri",
|
||||||
|
"stream_keys",
|
||||||
|
"custom_cache_key",
|
||||||
|
"data",
|
||||||
|
"state",
|
||||||
|
"start_time_ms",
|
||||||
|
"update_time_ms",
|
||||||
|
"content_length",
|
||||||
|
"stop_reason",
|
||||||
|
"failure_reason",
|
||||||
|
"percent_downloaded",
|
||||||
|
"bytes_downloaded"
|
||||||
|
};
|
||||||
|
try (Cursor cursor =
|
||||||
|
database.query(
|
||||||
|
tableName,
|
||||||
|
columnsV2,
|
||||||
|
/* selection= */ null,
|
||||||
|
/* selectionArgs= */ null,
|
||||||
|
/* groupBy= */ null,
|
||||||
|
/* having= */ null,
|
||||||
|
/* orderBy= */ null); ) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
downloads.add(getDownloadForCurrentRowV2(cursor));
|
||||||
|
}
|
||||||
|
return downloads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Infers the MIME type from a v2 table row. */
|
||||||
|
private static String inferMimeType(String downloadType) {
|
||||||
|
if ("dash".equals(downloadType)) {
|
||||||
|
return MimeTypes.APPLICATION_MPD;
|
||||||
|
} else if ("hls".equals(downloadType)) {
|
||||||
|
return MimeTypes.APPLICATION_M3U8;
|
||||||
|
} else if ("ss".equals(downloadType)) {
|
||||||
|
return MimeTypes.APPLICATION_SS;
|
||||||
|
} else {
|
||||||
|
return MimeTypes.VIDEO_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Cursor getCursor(String selection, @Nullable String[] selectionArgs)
|
private Cursor getCursor(String selection, @Nullable String[] selectionArgs)
|
||||||
throws DatabaseIOException {
|
throws DatabaseIOException {
|
||||||
try {
|
try {
|
||||||
|
|
@ -326,6 +394,25 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
/* package*/ static String encodeStreamKeys(List<StreamKey> streamKeys) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < streamKeys.size(); i++) {
|
||||||
|
StreamKey streamKey = streamKeys.get(i);
|
||||||
|
stringBuilder
|
||||||
|
.append(streamKey.periodIndex)
|
||||||
|
.append('.')
|
||||||
|
.append(streamKey.groupIndex)
|
||||||
|
.append('.')
|
||||||
|
.append(streamKey.trackIndex)
|
||||||
|
.append(',');
|
||||||
|
}
|
||||||
|
if (stringBuilder.length() > 0) {
|
||||||
|
stringBuilder.setLength(stringBuilder.length() - 1);
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private static String getStateQuery(@Download.State int... states) {
|
private static String getStateQuery(@Download.State int... states) {
|
||||||
if (states.length == 0) {
|
if (states.length == 0) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
@ -346,9 +433,10 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
DownloadRequest request =
|
DownloadRequest request =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
/* id= */ cursor.getString(COLUMN_INDEX_ID),
|
/* id= */ cursor.getString(COLUMN_INDEX_ID),
|
||||||
/* type= */ cursor.getString(COLUMN_INDEX_TYPE),
|
|
||||||
/* uri= */ Uri.parse(cursor.getString(COLUMN_INDEX_URI)),
|
/* uri= */ Uri.parse(cursor.getString(COLUMN_INDEX_URI)),
|
||||||
|
/* mimeType= */ cursor.getString(COLUMN_INDEX_MIME_TYPE),
|
||||||
/* streamKeys= */ decodeStreamKeys(cursor.getString(COLUMN_INDEX_STREAM_KEYS)),
|
/* streamKeys= */ decodeStreamKeys(cursor.getString(COLUMN_INDEX_STREAM_KEYS)),
|
||||||
|
/* keySetId= */ cursor.getBlob(COLUMN_INDEX_KEY_SET_ID),
|
||||||
/* customCacheKey= */ cursor.getString(COLUMN_INDEX_CUSTOM_CACHE_KEY),
|
/* customCacheKey= */ cursor.getString(COLUMN_INDEX_CUSTOM_CACHE_KEY),
|
||||||
/* data= */ cursor.getBlob(COLUMN_INDEX_DATA));
|
/* data= */ cursor.getBlob(COLUMN_INDEX_DATA));
|
||||||
DownloadProgress downloadProgress = new DownloadProgress();
|
DownloadProgress downloadProgress = new DownloadProgress();
|
||||||
|
|
@ -373,22 +461,53 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex {
|
||||||
downloadProgress);
|
downloadProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String encodeStreamKeys(List<StreamKey> streamKeys) {
|
/** Read a {@link Download} from a table row of version 2. */
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
private static Download getDownloadForCurrentRowV2(Cursor cursor) {
|
||||||
for (int i = 0; i < streamKeys.size(); i++) {
|
/*
|
||||||
StreamKey streamKey = streamKeys.get(i);
|
* Version 2 schema
|
||||||
stringBuilder
|
* Index Column Type
|
||||||
.append(streamKey.periodIndex)
|
* 0 id string
|
||||||
.append('.')
|
* 1 type string
|
||||||
.append(streamKey.groupIndex)
|
* 2 uri string
|
||||||
.append('.')
|
* 3 stream_keys string
|
||||||
.append(streamKey.trackIndex)
|
* 4 custom_cache_key string
|
||||||
.append(',');
|
* 5 data blob
|
||||||
}
|
* 6 state integer
|
||||||
if (stringBuilder.length() > 0) {
|
* 7 start_time_ms integer
|
||||||
stringBuilder.setLength(stringBuilder.length() - 1);
|
* 8 update_time_ms integer
|
||||||
}
|
* 9 content_length integer
|
||||||
return stringBuilder.toString();
|
* 10 stop_reason integer
|
||||||
|
* 11 failure_reason integer
|
||||||
|
* 12 percent_downloaded real
|
||||||
|
* 13 bytes_downloaded integer
|
||||||
|
*/
|
||||||
|
DownloadRequest request =
|
||||||
|
new DownloadRequest(
|
||||||
|
/* id= */ cursor.getString(0),
|
||||||
|
/* uri= */ Uri.parse(cursor.getString(2)),
|
||||||
|
/* mimeType= */ inferMimeType(cursor.getString(1)),
|
||||||
|
/* streamKeys= */ decodeStreamKeys(cursor.getString(3)),
|
||||||
|
/* keySetId= */ null,
|
||||||
|
/* customCacheKey= */ cursor.getString(4),
|
||||||
|
/* data= */ cursor.getBlob(5));
|
||||||
|
DownloadProgress downloadProgress = new DownloadProgress();
|
||||||
|
downloadProgress.bytesDownloaded = cursor.getLong(13);
|
||||||
|
downloadProgress.percentDownloaded = cursor.getFloat(12);
|
||||||
|
@State int state = cursor.getInt(6);
|
||||||
|
// It's possible the database contains failure reasons for non-failed downloads, which is
|
||||||
|
// invalid. Clear them here. See https://github.com/google/ExoPlayer/issues/6785.
|
||||||
|
@FailureReason
|
||||||
|
int failureReason =
|
||||||
|
state == Download.STATE_FAILED ? cursor.getInt(11) : Download.FAILURE_REASON_NONE;
|
||||||
|
return new Download(
|
||||||
|
request,
|
||||||
|
state,
|
||||||
|
/* startTimeMs= */ cursor.getLong(7),
|
||||||
|
/* updateTimeMs= */ cursor.getLong(8),
|
||||||
|
/* contentLength= */ cursor.getLong(9),
|
||||||
|
/* stopReason= */ cursor.getInt(10),
|
||||||
|
failureReason,
|
||||||
|
downloadProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<StreamKey> decodeStreamKeys(String encodedStreamKeys) {
|
private static List<StreamKey> decodeStreamKeys(String encodedStreamKeys) {
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,13 @@
|
||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.util.SparseArray;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
@ -31,46 +34,8 @@ import java.util.concurrent.Executor;
|
||||||
*/
|
*/
|
||||||
public class DefaultDownloaderFactory implements DownloaderFactory {
|
public class DefaultDownloaderFactory implements DownloaderFactory {
|
||||||
|
|
||||||
@Nullable private static final Constructor<? extends Downloader> DASH_DOWNLOADER_CONSTRUCTOR;
|
private static final SparseArray<Constructor<? extends Downloader>> CONSTRUCTORS =
|
||||||
@Nullable private static final Constructor<? extends Downloader> HLS_DOWNLOADER_CONSTRUCTOR;
|
createDownloaderConstructors();
|
||||||
@Nullable private static final Constructor<? extends Downloader> SS_DOWNLOADER_CONSTRUCTOR;
|
|
||||||
|
|
||||||
static {
|
|
||||||
@Nullable Constructor<? extends Downloader> dashDownloaderConstructor = null;
|
|
||||||
try {
|
|
||||||
// LINT.IfChange
|
|
||||||
dashDownloaderConstructor =
|
|
||||||
getDownloaderConstructor(
|
|
||||||
Class.forName("com.google.android.exoplayer2.source.dash.offline.DashDownloader"));
|
|
||||||
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// Expected if the app was built without the DASH module.
|
|
||||||
}
|
|
||||||
DASH_DOWNLOADER_CONSTRUCTOR = dashDownloaderConstructor;
|
|
||||||
@Nullable Constructor<? extends Downloader> hlsDownloaderConstructor = null;
|
|
||||||
try {
|
|
||||||
// LINT.IfChange
|
|
||||||
hlsDownloaderConstructor =
|
|
||||||
getDownloaderConstructor(
|
|
||||||
Class.forName("com.google.android.exoplayer2.source.hls.offline.HlsDownloader"));
|
|
||||||
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// Expected if the app was built without the HLS module.
|
|
||||||
}
|
|
||||||
HLS_DOWNLOADER_CONSTRUCTOR = hlsDownloaderConstructor;
|
|
||||||
@Nullable Constructor<? extends Downloader> ssDownloaderConstructor = null;
|
|
||||||
try {
|
|
||||||
// LINT.IfChange
|
|
||||||
ssDownloaderConstructor =
|
|
||||||
getDownloaderConstructor(
|
|
||||||
Class.forName(
|
|
||||||
"com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloader"));
|
|
||||||
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// Expected if the app was built without the SmoothStreaming module.
|
|
||||||
}
|
|
||||||
SS_DOWNLOADER_CONSTRUCTOR = ssDownloaderConstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final CacheDataSource.Factory cacheDataSourceFactory;
|
private final CacheDataSource.Factory cacheDataSourceFactory;
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
|
|
@ -105,8 +70,14 @@ public class DefaultDownloaderFactory implements DownloaderFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Downloader createDownloader(DownloadRequest request) {
|
public Downloader createDownloader(DownloadRequest request) {
|
||||||
switch (request.type) {
|
@C.ContentType
|
||||||
case DownloadRequest.TYPE_PROGRESSIVE:
|
int contentType = Util.inferContentTypeWithMimeType(request.uri, request.mimeType);
|
||||||
|
switch (contentType) {
|
||||||
|
case C.TYPE_DASH:
|
||||||
|
case C.TYPE_HLS:
|
||||||
|
case C.TYPE_SS:
|
||||||
|
return createDownloader(request, contentType);
|
||||||
|
case C.TYPE_OTHER:
|
||||||
return new ProgressiveDownloader(
|
return new ProgressiveDownloader(
|
||||||
new MediaItem.Builder()
|
new MediaItem.Builder()
|
||||||
.setUri(request.uri)
|
.setUri(request.uri)
|
||||||
|
|
@ -114,31 +85,57 @@ public class DefaultDownloaderFactory implements DownloaderFactory {
|
||||||
.build(),
|
.build(),
|
||||||
cacheDataSourceFactory,
|
cacheDataSourceFactory,
|
||||||
executor);
|
executor);
|
||||||
case DownloadRequest.TYPE_DASH:
|
|
||||||
return createDownloader(request, DASH_DOWNLOADER_CONSTRUCTOR);
|
|
||||||
case DownloadRequest.TYPE_HLS:
|
|
||||||
return createDownloader(request, HLS_DOWNLOADER_CONSTRUCTOR);
|
|
||||||
case DownloadRequest.TYPE_SS:
|
|
||||||
return createDownloader(request, SS_DOWNLOADER_CONSTRUCTOR);
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unsupported type: " + request.type);
|
throw new IllegalArgumentException("Unsupported type: " + contentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Downloader createDownloader(
|
private Downloader createDownloader(DownloadRequest request, @C.ContentType int contentType) {
|
||||||
DownloadRequest request, @Nullable Constructor<? extends Downloader> constructor) {
|
@Nullable Constructor<? extends Downloader> constructor = CONSTRUCTORS.get(contentType);
|
||||||
if (constructor == null) {
|
if (constructor == null) {
|
||||||
throw new IllegalStateException("Module missing for: " + request.type);
|
throw new IllegalStateException("Module missing for content type " + contentType);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return constructor.newInstance(
|
return constructor.newInstance(
|
||||||
request.uri, request.streamKeys, cacheDataSourceFactory, executor);
|
request.uri, request.streamKeys, cacheDataSourceFactory, executor);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to instantiate downloader for: " + request.type, e);
|
throw new IllegalStateException(
|
||||||
|
"Failed to instantiate downloader for content type " + contentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LINT.IfChange
|
// LINT.IfChange
|
||||||
|
private static SparseArray<Constructor<? extends Downloader>> createDownloaderConstructors() {
|
||||||
|
SparseArray<Constructor<? extends Downloader>> array = new SparseArray<>();
|
||||||
|
try {
|
||||||
|
array.put(
|
||||||
|
C.TYPE_DASH,
|
||||||
|
getDownloaderConstructor(
|
||||||
|
Class.forName("com.google.android.exoplayer2.source.dash.offline.DashDownloader")));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Expected if the app was built without the DASH module.
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.put(
|
||||||
|
C.TYPE_HLS,
|
||||||
|
getDownloaderConstructor(
|
||||||
|
Class.forName("com.google.android.exoplayer2.source.hls.offline.HlsDownloader")));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Expected if the app was built without the HLS module.
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
array.put(
|
||||||
|
C.TYPE_SS,
|
||||||
|
getDownloaderConstructor(
|
||||||
|
Class.forName(
|
||||||
|
"com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloader")));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Expected if the app was built without the SmoothStreaming module.
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
private static Constructor<? extends Downloader> getDownloaderConstructor(Class<?> clazz) {
|
private static Constructor<? extends Downloader> getDownloaderConstructor(Class<?> clazz) {
|
||||||
try {
|
try {
|
||||||
return clazz
|
return clazz
|
||||||
|
|
@ -146,7 +143,7 @@ public class DefaultDownloaderFactory implements DownloaderFactory {
|
||||||
.getConstructor(Uri.class, List.class, CacheDataSource.Factory.class, Executor.class);
|
.getConstructor(Uri.class, List.class, CacheDataSource.Factory.class, Executor.class);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
// The downloader is present, but the expected constructor is missing.
|
// The downloader is present, but the expected constructor is missing.
|
||||||
throw new RuntimeException("Downloader constructor missing", e);
|
throw new IllegalStateException("Downloader constructor missing", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
|
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
|
||||||
|
|
|
||||||
|
|
@ -320,9 +320,7 @@ public final class DownloadHelper {
|
||||||
* @throws IllegalStateException If the media item is of type DASH, HLS or SmoothStreaming.
|
* @throws IllegalStateException If the media item is of type DASH, HLS or SmoothStreaming.
|
||||||
*/
|
*/
|
||||||
public static DownloadHelper forMediaItem(Context context, MediaItem mediaItem) {
|
public static DownloadHelper forMediaItem(Context context, MediaItem mediaItem) {
|
||||||
Assertions.checkArgument(
|
Assertions.checkArgument(isProgressive(checkNotNull(mediaItem.playbackProperties)));
|
||||||
DownloadRequest.TYPE_PROGRESSIVE.equals(
|
|
||||||
getDownloadType(checkNotNull(mediaItem.playbackProperties))));
|
|
||||||
return forMediaItem(
|
return forMediaItem(
|
||||||
mediaItem,
|
mediaItem,
|
||||||
getDefaultTrackSelectorParameters(context),
|
getDefaultTrackSelectorParameters(context),
|
||||||
|
|
@ -412,9 +410,7 @@ public final class DownloadHelper {
|
||||||
@Nullable RenderersFactory renderersFactory,
|
@Nullable RenderersFactory renderersFactory,
|
||||||
@Nullable DataSource.Factory dataSourceFactory,
|
@Nullable DataSource.Factory dataSourceFactory,
|
||||||
@Nullable DrmSessionManager drmSessionManager) {
|
@Nullable DrmSessionManager drmSessionManager) {
|
||||||
boolean isProgressive =
|
boolean isProgressive = isProgressive(checkNotNull(mediaItem.playbackProperties));
|
||||||
DownloadRequest.TYPE_PROGRESSIVE.equals(
|
|
||||||
getDownloadType(checkNotNull(mediaItem.playbackProperties)));
|
|
||||||
Assertions.checkArgument(isProgressive || dataSourceFactory != null);
|
Assertions.checkArgument(isProgressive || dataSourceFactory != null);
|
||||||
return new DownloadHelper(
|
return new DownloadHelper(
|
||||||
mediaItem,
|
mediaItem,
|
||||||
|
|
@ -455,7 +451,7 @@ public final class DownloadHelper {
|
||||||
new MediaItem.Builder()
|
new MediaItem.Builder()
|
||||||
.setUri(downloadRequest.uri)
|
.setUri(downloadRequest.uri)
|
||||||
.setCustomCacheKey(downloadRequest.customCacheKey)
|
.setCustomCacheKey(downloadRequest.customCacheKey)
|
||||||
.setMimeType(getMimeType(downloadRequest.type))
|
.setMimeType(downloadRequest.mimeType)
|
||||||
.setStreamKeys(downloadRequest.streamKeys)
|
.setStreamKeys(downloadRequest.streamKeys)
|
||||||
.build(),
|
.build(),
|
||||||
dataSourceFactory,
|
dataSourceFactory,
|
||||||
|
|
@ -747,13 +743,14 @@ public final class DownloadHelper {
|
||||||
* @return The built {@link DownloadRequest}.
|
* @return The built {@link DownloadRequest}.
|
||||||
*/
|
*/
|
||||||
public DownloadRequest getDownloadRequest(String id, @Nullable byte[] data) {
|
public DownloadRequest getDownloadRequest(String id, @Nullable byte[] data) {
|
||||||
String downloadType = getDownloadType(playbackProperties);
|
|
||||||
if (mediaSource == null) {
|
if (mediaSource == null) {
|
||||||
|
// TODO: add support for DRM (keySetId) [Internal ref: b/158980798]
|
||||||
return new DownloadRequest(
|
return new DownloadRequest(
|
||||||
id,
|
id,
|
||||||
downloadType,
|
|
||||||
playbackProperties.uri,
|
playbackProperties.uri,
|
||||||
|
playbackProperties.mimeType,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
|
/* keySetId= */ null,
|
||||||
playbackProperties.customCacheKey,
|
playbackProperties.customCacheKey,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
@ -769,11 +766,13 @@ public final class DownloadHelper {
|
||||||
}
|
}
|
||||||
streamKeys.addAll(mediaPreparer.mediaPeriods[periodIndex].getStreamKeys(allSelections));
|
streamKeys.addAll(mediaPreparer.mediaPeriods[periodIndex].getStreamKeys(allSelections));
|
||||||
}
|
}
|
||||||
|
// TODO: add support for DRM (keySetId) [Internal ref: b/158980798]
|
||||||
return new DownloadRequest(
|
return new DownloadRequest(
|
||||||
id,
|
id,
|
||||||
downloadType,
|
|
||||||
playbackProperties.uri,
|
playbackProperties.uri,
|
||||||
|
playbackProperties.mimeType,
|
||||||
streamKeys,
|
streamKeys,
|
||||||
|
/* keySetId= */ null,
|
||||||
playbackProperties.customCacheKey,
|
playbackProperties.customCacheKey,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
@ -909,36 +908,9 @@ public final class DownloadHelper {
|
||||||
.createMediaSource(mediaItem);
|
.createMediaSource(mediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getDownloadType(MediaItem.PlaybackProperties playbackProperties) {
|
private static boolean isProgressive(MediaItem.PlaybackProperties playbackProperties) {
|
||||||
int contentType =
|
return Util.inferContentTypeWithMimeType(playbackProperties.uri, playbackProperties.mimeType)
|
||||||
Util.inferContentTypeWithMimeType(playbackProperties.uri, playbackProperties.mimeType);
|
== C.TYPE_OTHER;
|
||||||
switch (contentType) {
|
|
||||||
case C.TYPE_DASH:
|
|
||||||
return DownloadRequest.TYPE_DASH;
|
|
||||||
case C.TYPE_HLS:
|
|
||||||
return DownloadRequest.TYPE_HLS;
|
|
||||||
case C.TYPE_SS:
|
|
||||||
return DownloadRequest.TYPE_SS;
|
|
||||||
case C.TYPE_OTHER:
|
|
||||||
default:
|
|
||||||
return DownloadRequest.TYPE_PROGRESSIVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static String getMimeType(String downloadType) {
|
|
||||||
switch (downloadType) {
|
|
||||||
case DownloadRequest.TYPE_DASH:
|
|
||||||
return MimeTypes.APPLICATION_MPD;
|
|
||||||
case DownloadRequest.TYPE_HLS:
|
|
||||||
return MimeTypes.APPLICATION_M3U8;
|
|
||||||
case DownloadRequest.TYPE_SS:
|
|
||||||
return MimeTypes.APPLICATION_SS;
|
|
||||||
case DownloadRequest.TYPE_PROGRESSIVE:
|
|
||||||
return null;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class MediaPreparer
|
private static final class MediaPreparer
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import android.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -35,23 +36,23 @@ public final class DownloadRequest implements Parcelable {
|
||||||
/** Thrown when the encoded request data belongs to an unsupported request type. */
|
/** Thrown when the encoded request data belongs to an unsupported request type. */
|
||||||
public static class UnsupportedRequestException extends IOException {}
|
public static class UnsupportedRequestException extends IOException {}
|
||||||
|
|
||||||
/** Type for progressive downloads. */
|
|
||||||
public static final String TYPE_PROGRESSIVE = "progressive";
|
|
||||||
/** Type for DASH downloads. */
|
|
||||||
public static final String TYPE_DASH = "dash";
|
|
||||||
/** Type for HLS downloads. */
|
|
||||||
public static final String TYPE_HLS = "hls";
|
|
||||||
/** Type for SmoothStreaming downloads. */
|
|
||||||
public static final String TYPE_SS = "ss";
|
|
||||||
|
|
||||||
/** The unique content id. */
|
/** The unique content id. */
|
||||||
public final String id;
|
public final String id;
|
||||||
/** The type of the request. */
|
|
||||||
public final String type;
|
|
||||||
/** The uri being downloaded. */
|
/** The uri being downloaded. */
|
||||||
public final Uri uri;
|
public final Uri uri;
|
||||||
|
/**
|
||||||
|
* The MIME type of this content. Used as a hint to infer the content's type (DASH, HLS,
|
||||||
|
* SmoothStreaming). If null, a {@link DownloadService} will infer the content type from the
|
||||||
|
* {@link #uri}.
|
||||||
|
*/
|
||||||
|
@Nullable public final String mimeType;
|
||||||
/** Stream keys to be downloaded. If empty, all streams will be downloaded. */
|
/** Stream keys to be downloaded. If empty, all streams will be downloaded. */
|
||||||
public final List<StreamKey> streamKeys;
|
public final List<StreamKey> streamKeys;
|
||||||
|
/**
|
||||||
|
* The key set id of the offline licence if the content is protected with DRM, or empty if no
|
||||||
|
* license is needed.
|
||||||
|
*/
|
||||||
|
public final byte[] keySetId;
|
||||||
/**
|
/**
|
||||||
* Custom key for cache indexing, or null. Must be null for DASH, HLS and SmoothStreaming
|
* Custom key for cache indexing, or null. Must be null for DASH, HLS and SmoothStreaming
|
||||||
* downloads.
|
* downloads.
|
||||||
|
|
@ -62,43 +63,48 @@ public final class DownloadRequest implements Parcelable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id See {@link #id}.
|
* @param id See {@link #id}.
|
||||||
* @param type See {@link #type}.
|
|
||||||
* @param uri See {@link #uri}.
|
* @param uri See {@link #uri}.
|
||||||
|
* @param mimeType See {@link #mimeType}
|
||||||
* @param streamKeys See {@link #streamKeys}.
|
* @param streamKeys See {@link #streamKeys}.
|
||||||
* @param customCacheKey See {@link #customCacheKey}.
|
* @param customCacheKey See {@link #customCacheKey}.
|
||||||
* @param data See {@link #data}.
|
* @param data See {@link #data}.
|
||||||
*/
|
*/
|
||||||
public DownloadRequest(
|
public DownloadRequest(
|
||||||
String id,
|
String id,
|
||||||
String type,
|
|
||||||
Uri uri,
|
Uri uri,
|
||||||
|
@Nullable String mimeType,
|
||||||
List<StreamKey> streamKeys,
|
List<StreamKey> streamKeys,
|
||||||
|
@Nullable byte[] keySetId,
|
||||||
@Nullable String customCacheKey,
|
@Nullable String customCacheKey,
|
||||||
@Nullable byte[] data) {
|
@Nullable byte[] data) {
|
||||||
if (TYPE_DASH.equals(type) || TYPE_HLS.equals(type) || TYPE_SS.equals(type)) {
|
@C.ContentType int contentType = Util.inferContentTypeWithMimeType(uri, mimeType);
|
||||||
|
if (contentType == C.TYPE_DASH || contentType == C.TYPE_HLS || contentType == C.TYPE_SS) {
|
||||||
Assertions.checkArgument(
|
Assertions.checkArgument(
|
||||||
customCacheKey == null, "customCacheKey must be null for type: " + type);
|
customCacheKey == null, "customCacheKey must be null for type: " + contentType);
|
||||||
}
|
}
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
|
this.mimeType = mimeType;
|
||||||
ArrayList<StreamKey> mutableKeys = new ArrayList<>(streamKeys);
|
ArrayList<StreamKey> mutableKeys = new ArrayList<>(streamKeys);
|
||||||
Collections.sort(mutableKeys);
|
Collections.sort(mutableKeys);
|
||||||
this.streamKeys = Collections.unmodifiableList(mutableKeys);
|
this.streamKeys = Collections.unmodifiableList(mutableKeys);
|
||||||
|
this.keySetId =
|
||||||
|
keySetId != null ? Arrays.copyOf(keySetId, keySetId.length) : Util.EMPTY_BYTE_ARRAY;
|
||||||
this.customCacheKey = customCacheKey;
|
this.customCacheKey = customCacheKey;
|
||||||
this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY;
|
this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ DownloadRequest(Parcel in) {
|
/* package */ DownloadRequest(Parcel in) {
|
||||||
id = castNonNull(in.readString());
|
id = castNonNull(in.readString());
|
||||||
type = castNonNull(in.readString());
|
|
||||||
uri = Uri.parse(castNonNull(in.readString()));
|
uri = Uri.parse(castNonNull(in.readString()));
|
||||||
|
mimeType = in.readString();
|
||||||
int streamKeyCount = in.readInt();
|
int streamKeyCount = in.readInt();
|
||||||
ArrayList<StreamKey> mutableStreamKeys = new ArrayList<>(streamKeyCount);
|
ArrayList<StreamKey> mutableStreamKeys = new ArrayList<>(streamKeyCount);
|
||||||
for (int i = 0; i < streamKeyCount; i++) {
|
for (int i = 0; i < streamKeyCount; i++) {
|
||||||
mutableStreamKeys.add(in.readParcelable(StreamKey.class.getClassLoader()));
|
mutableStreamKeys.add(in.readParcelable(StreamKey.class.getClassLoader()));
|
||||||
}
|
}
|
||||||
streamKeys = Collections.unmodifiableList(mutableStreamKeys);
|
streamKeys = Collections.unmodifiableList(mutableStreamKeys);
|
||||||
|
keySetId = castNonNull(in.createByteArray());
|
||||||
customCacheKey = in.readString();
|
customCacheKey = in.readString();
|
||||||
data = castNonNull(in.createByteArray());
|
data = castNonNull(in.createByteArray());
|
||||||
}
|
}
|
||||||
|
|
@ -110,24 +116,22 @@ public final class DownloadRequest implements Parcelable {
|
||||||
* @return The copy with the specified ID.
|
* @return The copy with the specified ID.
|
||||||
*/
|
*/
|
||||||
public DownloadRequest copyWithId(String id) {
|
public DownloadRequest copyWithId(String id) {
|
||||||
return new DownloadRequest(id, type, uri, streamKeys, customCacheKey, data);
|
return new DownloadRequest(id, uri, mimeType, streamKeys, keySetId, customCacheKey, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the result of merging {@code newRequest} into this request. The requests must have the
|
* Returns the result of merging {@code newRequest} into this request. The requests must have the
|
||||||
* same {@link #id} and {@link #type}.
|
* same {@link #id}.
|
||||||
*
|
*
|
||||||
* <p>If the requests have different {@link #uri}, {@link #customCacheKey} and {@link #data}
|
* <p>The resulting request contains the stream keys from both requests. For all other member
|
||||||
* values, then those from the request being merged are included in the result.
|
* variables, those in {@code newRequest} are preferred.
|
||||||
*
|
*
|
||||||
* @param newRequest The request being merged.
|
* @param newRequest The request being merged.
|
||||||
* @return The merged result.
|
* @return The merged result.
|
||||||
* @throws IllegalArgumentException If the requests do not have the same {@link #id} and {@link
|
* @throws IllegalArgumentException If the requests do not have the same {@link #id}.
|
||||||
* #type}.
|
|
||||||
*/
|
*/
|
||||||
public DownloadRequest copyWithMergedRequest(DownloadRequest newRequest) {
|
public DownloadRequest copyWithMergedRequest(DownloadRequest newRequest) {
|
||||||
Assertions.checkArgument(id.equals(newRequest.id));
|
Assertions.checkArgument(id.equals(newRequest.id));
|
||||||
Assertions.checkArgument(type.equals(newRequest.type));
|
|
||||||
List<StreamKey> mergedKeys;
|
List<StreamKey> mergedKeys;
|
||||||
if (streamKeys.isEmpty() || newRequest.streamKeys.isEmpty()) {
|
if (streamKeys.isEmpty() || newRequest.streamKeys.isEmpty()) {
|
||||||
// If either streamKeys is empty then all streams should be downloaded.
|
// If either streamKeys is empty then all streams should be downloaded.
|
||||||
|
|
@ -142,12 +146,18 @@ public final class DownloadRequest implements Parcelable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DownloadRequest(
|
return new DownloadRequest(
|
||||||
id, type, newRequest.uri, mergedKeys, newRequest.customCacheKey, newRequest.data);
|
id,
|
||||||
|
newRequest.uri,
|
||||||
|
newRequest.mimeType,
|
||||||
|
mergedKeys,
|
||||||
|
newRequest.keySetId,
|
||||||
|
newRequest.customCacheKey,
|
||||||
|
newRequest.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return type + ":" + id;
|
return mimeType + ":" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -157,20 +167,21 @@ public final class DownloadRequest implements Parcelable {
|
||||||
}
|
}
|
||||||
DownloadRequest that = (DownloadRequest) o;
|
DownloadRequest that = (DownloadRequest) o;
|
||||||
return id.equals(that.id)
|
return id.equals(that.id)
|
||||||
&& type.equals(that.type)
|
|
||||||
&& uri.equals(that.uri)
|
&& uri.equals(that.uri)
|
||||||
|
&& Util.areEqual(mimeType, that.mimeType)
|
||||||
&& streamKeys.equals(that.streamKeys)
|
&& streamKeys.equals(that.streamKeys)
|
||||||
|
&& Arrays.equals(keySetId, that.keySetId)
|
||||||
&& Util.areEqual(customCacheKey, that.customCacheKey)
|
&& Util.areEqual(customCacheKey, that.customCacheKey)
|
||||||
&& Arrays.equals(data, that.data);
|
&& Arrays.equals(data, that.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int hashCode() {
|
public final int hashCode() {
|
||||||
int result = type.hashCode();
|
int result = 31 * id.hashCode();
|
||||||
result = 31 * result + id.hashCode();
|
|
||||||
result = 31 * result + type.hashCode();
|
|
||||||
result = 31 * result + uri.hashCode();
|
result = 31 * result + uri.hashCode();
|
||||||
|
result = 31 * result + (mimeType != null ? mimeType.hashCode() : 0);
|
||||||
result = 31 * result + streamKeys.hashCode();
|
result = 31 * result + streamKeys.hashCode();
|
||||||
|
result = 31 * result + Arrays.hashCode(keySetId);
|
||||||
result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0);
|
result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0);
|
||||||
result = 31 * result + Arrays.hashCode(data);
|
result = 31 * result + Arrays.hashCode(data);
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -186,12 +197,13 @@ public final class DownloadRequest implements Parcelable {
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeString(id);
|
dest.writeString(id);
|
||||||
dest.writeString(type);
|
|
||||||
dest.writeString(uri.toString());
|
dest.writeString(uri.toString());
|
||||||
|
dest.writeString(mimeType);
|
||||||
dest.writeInt(streamKeys.size());
|
dest.writeInt(streamKeys.size());
|
||||||
for (int i = 0; i < streamKeys.size(); i++) {
|
for (int i = 0; i < streamKeys.size(); i++) {
|
||||||
dest.writeParcelable(streamKeys.get(i), /* parcelableFlags= */ 0);
|
dest.writeParcelable(streamKeys.get(i), /* parcelableFlags= */ 0);
|
||||||
}
|
}
|
||||||
|
dest.writeByteArray(keySetId);
|
||||||
dest.writeString(customCacheKey);
|
dest.writeString(customCacheKey);
|
||||||
dest.writeByteArray(data);
|
dest.writeByteArray(data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,16 +85,4 @@ public class VersionTableTest {
|
||||||
.isEqualTo(VersionTable.VERSION_UNSET);
|
.isEqualTo(VersionTable.VERSION_UNSET);
|
||||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_2)).isEqualTo(2);
|
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_2)).isEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void doesTableExist_nonExistingTable_returnsFalse() {
|
|
||||||
assertThat(VersionTable.tableExists(database, "NonExistingTable")).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void doesTableExist_existingTable_returnsTrue() {
|
|
||||||
String table = "TestTable";
|
|
||||||
databaseProvider.getWritableDatabase().execSQL("CREATE TABLE " + table + " (test INTEGER)");
|
|
||||||
assertThat(VersionTable.tableExists(database, table)).isTrue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import android.net.Uri;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
|
@ -127,9 +128,10 @@ public class ActionFileTest {
|
||||||
private static DownloadRequest buildExpectedRequest(Uri uri, byte[] data) {
|
private static DownloadRequest buildExpectedRequest(Uri uri, byte[] data) {
|
||||||
return new DownloadRequest(
|
return new DownloadRequest(
|
||||||
/* id= */ uri.toString(),
|
/* id= */ uri.toString(),
|
||||||
DownloadRequest.TYPE_PROGRESSIVE,
|
|
||||||
uri,
|
uri,
|
||||||
|
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_PROGRESSIVE;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
@ -23,6 +22,7 @@ import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
|
@ -74,18 +74,20 @@ public class ActionFileUpgradeUtilTest {
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
||||||
DownloadRequest expectedRequest1 =
|
DownloadRequest expectedRequest1 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"key123",
|
/* id= */ "key123",
|
||||||
/* type= */ "test",
|
|
||||||
Uri.parse("https://www.test.com/download1"),
|
Uri.parse("https://www.test.com/download1"),
|
||||||
|
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
|
||||||
asList(expectedStreamKey1),
|
asList(expectedStreamKey1),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
new byte[] {1, 2, 3, 4});
|
/* data= */ new byte[] {1, 2, 3, 4});
|
||||||
DownloadRequest expectedRequest2 =
|
DownloadRequest expectedRequest2 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"key234",
|
/* id= */ "key234",
|
||||||
/* type= */ "test",
|
|
||||||
Uri.parse("https://www.test.com/download2"),
|
Uri.parse("https://www.test.com/download2"),
|
||||||
|
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
|
||||||
asList(expectedStreamKey2),
|
asList(expectedStreamKey2),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ "key234",
|
/* customCacheKey= */ "key234",
|
||||||
new byte[] {5, 4, 3, 2, 1});
|
new byte[] {5, 4, 3, 2, 1});
|
||||||
|
|
||||||
|
|
@ -102,17 +104,17 @@ public class ActionFileUpgradeUtilTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeRequest_nonExistingDownload_createsNewDownload() throws IOException {
|
public void mergeRequest_nonExistingDownload_createsNewDownload() throws IOException {
|
||||||
byte[] data = new byte[] {1, 2, 3, 4};
|
|
||||||
DownloadRequest request =
|
DownloadRequest request =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
/* id= */ "id",
|
||||||
TYPE_PROGRESSIVE,
|
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
/* mimeType= */ null,
|
||||||
asList(
|
asList(
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
|
||||||
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5)),
|
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5)),
|
||||||
|
/* keySetId= */ new byte[] {1, 2, 3, 4},
|
||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
data);
|
/* data= */ new byte[] {1, 2, 3, 4});
|
||||||
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
ActionFileUpgradeUtil.mergeRequest(
|
||||||
request, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
request, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
||||||
|
|
@ -128,32 +130,36 @@ public class ActionFileUpgradeUtilTest {
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
||||||
DownloadRequest request1 =
|
DownloadRequest request1 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
/* id= */ "id",
|
||||||
TYPE_PROGRESSIVE,
|
|
||||||
Uri.parse("https://www.test.com/download1"),
|
Uri.parse("https://www.test.com/download1"),
|
||||||
|
/* mimeType= */ null,
|
||||||
asList(streamKey1),
|
asList(streamKey1),
|
||||||
|
/* keySetId= */ new byte[] {1, 2, 3, 4},
|
||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
new byte[] {1, 2, 3, 4});
|
/* data= */ new byte[] {1, 2, 3, 4});
|
||||||
DownloadRequest request2 =
|
DownloadRequest request2 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
/* id= */ "id",
|
||||||
TYPE_PROGRESSIVE,
|
|
||||||
Uri.parse("https://www.test.com/download2"),
|
Uri.parse("https://www.test.com/download2"),
|
||||||
|
/* mimeType= */ MimeTypes.APPLICATION_MP4,
|
||||||
asList(streamKey2),
|
asList(streamKey2),
|
||||||
/* customCacheKey= */ "key123",
|
/* keySetId= */ new byte[] {5, 4, 3, 2, 1},
|
||||||
new byte[] {5, 4, 3, 2, 1});
|
/* customCacheKey= */ "key345",
|
||||||
|
/* data= */ new byte[] {5, 4, 3, 2, 1});
|
||||||
|
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
ActionFileUpgradeUtil.mergeRequest(
|
||||||
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
ActionFileUpgradeUtil.mergeRequest(
|
||||||
request2, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
request2, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
||||||
|
|
||||||
Download download = downloadIndex.getDownload(request2.id);
|
Download download = downloadIndex.getDownload(request2.id);
|
||||||
|
|
||||||
assertThat(download).isNotNull();
|
assertThat(download).isNotNull();
|
||||||
assertThat(download.request.type).isEqualTo(request2.type);
|
assertThat(download.request.mimeType).isEqualTo(MimeTypes.APPLICATION_MP4);
|
||||||
assertThat(download.request.customCacheKey).isEqualTo(request2.customCacheKey);
|
assertThat(download.request.customCacheKey).isEqualTo(request2.customCacheKey);
|
||||||
assertThat(download.request.data).isEqualTo(request2.data);
|
assertThat(download.request.data).isEqualTo(request2.data);
|
||||||
assertThat(download.request.uri).isEqualTo(request2.uri);
|
assertThat(download.request.uri).isEqualTo(request2.uri);
|
||||||
assertThat(download.request.streamKeys).containsExactly(streamKey1, streamKey2);
|
assertThat(download.request.streamKeys).containsExactly(streamKey1, streamKey2);
|
||||||
|
assertThat(download.request.keySetId).isEqualTo(request2.keySetId);
|
||||||
assertThat(download.state).isEqualTo(Download.STATE_QUEUED);
|
assertThat(download.state).isEqualTo(Download.STATE_QUEUED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,20 +171,22 @@ public class ActionFileUpgradeUtilTest {
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
||||||
DownloadRequest request1 =
|
DownloadRequest request1 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id1",
|
/* id= */ "id1",
|
||||||
TYPE_PROGRESSIVE,
|
|
||||||
Uri.parse("https://www.test.com/download1"),
|
Uri.parse("https://www.test.com/download1"),
|
||||||
|
/* mimeType= */ null,
|
||||||
asList(streamKey1),
|
asList(streamKey1),
|
||||||
|
/* keySetId= */ new byte[] {1, 2, 3, 4},
|
||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
new byte[] {1, 2, 3, 4});
|
/* data= */ new byte[] {1, 2, 3, 4});
|
||||||
DownloadRequest request2 =
|
DownloadRequest request2 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id2",
|
/* id= */ "id2",
|
||||||
TYPE_PROGRESSIVE,
|
|
||||||
Uri.parse("https://www.test.com/download2"),
|
Uri.parse("https://www.test.com/download2"),
|
||||||
|
/* mimeType= */ null,
|
||||||
asList(streamKey2),
|
asList(streamKey2),
|
||||||
|
/* keySetId= */ new byte[] {5, 4, 3, 2, 1},
|
||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
new byte[] {5, 4, 3, 2, 1});
|
/* data= */ new byte[] {5, 4, 3, 2, 1});
|
||||||
ActionFileUpgradeUtil.mergeRequest(
|
ActionFileUpgradeUtil.mergeRequest(
|
||||||
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,31 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.offline.Download.FAILURE_REASON_NONE;
|
||||||
|
import static com.google.android.exoplayer2.offline.Download.FAILURE_REASON_UNKNOWN;
|
||||||
|
import static com.google.android.exoplayer2.offline.Download.STATE_DOWNLOADING;
|
||||||
|
import static com.google.android.exoplayer2.offline.Download.STATE_STOPPED;
|
||||||
|
import static com.google.android.exoplayer2.offline.Download.STOP_REASON_NONE;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.net.Uri;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.database.DatabaseIOException;
|
import com.google.android.exoplayer2.database.DatabaseIOException;
|
||||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
||||||
import com.google.android.exoplayer2.database.VersionTable;
|
import com.google.android.exoplayer2.database.VersionTable;
|
||||||
import com.google.android.exoplayer2.testutil.DownloadBuilder;
|
import com.google.android.exoplayer2.testutil.DownloadBuilder;
|
||||||
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
@ -73,14 +89,14 @@ public class DefaultDownloadIndexTest {
|
||||||
|
|
||||||
Download download =
|
Download download =
|
||||||
downloadBuilder
|
downloadBuilder
|
||||||
.setType("different type")
|
|
||||||
.setUri("different uri")
|
.setUri("different uri")
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MP4)
|
||||||
.setCacheKey("different cacheKey")
|
.setCacheKey("different cacheKey")
|
||||||
.setState(Download.STATE_FAILED)
|
.setState(Download.STATE_FAILED)
|
||||||
.setPercentDownloaded(50)
|
.setPercentDownloaded(50)
|
||||||
.setBytesDownloaded(200)
|
.setBytesDownloaded(200)
|
||||||
.setContentLength(400)
|
.setContentLength(400)
|
||||||
.setFailureReason(Download.FAILURE_REASON_UNKNOWN)
|
.setFailureReason(FAILURE_REASON_UNKNOWN)
|
||||||
.setStopReason(0x12345678)
|
.setStopReason(0x12345678)
|
||||||
.setStartTimeMs(10)
|
.setStartTimeMs(10)
|
||||||
.setUpdateTimeMs(20)
|
.setUpdateTimeMs(20)
|
||||||
|
|
@ -88,6 +104,7 @@ public class DefaultDownloadIndexTest {
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
|
||||||
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5))
|
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5))
|
||||||
.setCustomMetadata(new byte[] {0, 1, 2, 3, 7, 8, 9, 10})
|
.setCustomMetadata(new byte[] {0, 1, 2, 3, 7, 8, 9, 10})
|
||||||
|
.setKeySetId(new byte[] {0, 1, 2, 3})
|
||||||
.build();
|
.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
Download readDownload = downloadIndex.getDownload(id);
|
Download readDownload = downloadIndex.getDownload(id);
|
||||||
|
|
@ -153,7 +170,7 @@ public class DefaultDownloadIndexTest {
|
||||||
new DownloadBuilder("id1").setStartTimeMs(0).setState(Download.STATE_REMOVING).build();
|
new DownloadBuilder("id1").setStartTimeMs(0).setState(Download.STATE_REMOVING).build();
|
||||||
downloadIndex.putDownload(download1);
|
downloadIndex.putDownload(download1);
|
||||||
Download download2 =
|
Download download2 =
|
||||||
new DownloadBuilder("id2").setStartTimeMs(1).setState(Download.STATE_STOPPED).build();
|
new DownloadBuilder("id2").setStartTimeMs(1).setState(STATE_STOPPED).build();
|
||||||
downloadIndex.putDownload(download2);
|
downloadIndex.putDownload(download2);
|
||||||
Download download3 =
|
Download download3 =
|
||||||
new DownloadBuilder("id3").setStartTimeMs(2).setState(Download.STATE_COMPLETED).build();
|
new DownloadBuilder("id3").setStartTimeMs(2).setState(Download.STATE_COMPLETED).build();
|
||||||
|
|
@ -202,6 +219,47 @@ public class DefaultDownloadIndexTest {
|
||||||
.isEqualTo(DefaultDownloadIndex.TABLE_VERSION);
|
.isEqualTo(DefaultDownloadIndex.TABLE_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void downloadIndex_upgradesFromVersion2() throws IOException {
|
||||||
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
File databaseFile = context.getDatabasePath(ExoDatabaseProvider.DATABASE_NAME);
|
||||||
|
try (FileOutputStream output = new FileOutputStream(databaseFile)) {
|
||||||
|
output.write(TestUtil.getByteArray(context, "offline/exoplayer_internal_v2.db"));
|
||||||
|
}
|
||||||
|
Download dashDownload =
|
||||||
|
createDownload(
|
||||||
|
/* uri= */ "http://www.test.com/manifest.mpd",
|
||||||
|
/* mimeType= */ MimeTypes.APPLICATION_MPD,
|
||||||
|
ImmutableList.of(),
|
||||||
|
/* customCacheKey= */ null);
|
||||||
|
Download hlsDownload =
|
||||||
|
createDownload(
|
||||||
|
/* uri= */ "http://www.test.com/manifest.m3u8",
|
||||||
|
/* mimeType= */ MimeTypes.APPLICATION_M3U8,
|
||||||
|
ImmutableList.of(),
|
||||||
|
/* customCacheKey= */ null);
|
||||||
|
Download ssDownload =
|
||||||
|
createDownload(
|
||||||
|
/* uri= */ "http://www.test.com/video.ism/manifest",
|
||||||
|
/* mimeType= */ MimeTypes.APPLICATION_SS,
|
||||||
|
Arrays.asList(new StreamKey(0, 0), new StreamKey(1, 1)),
|
||||||
|
/* customCacheKey= */ null);
|
||||||
|
Download progressiveDownload =
|
||||||
|
createDownload(
|
||||||
|
/* uri= */ "http://www.test.com/video.mp4",
|
||||||
|
/* mimeType= */ MimeTypes.VIDEO_UNKNOWN,
|
||||||
|
ImmutableList.of(),
|
||||||
|
/* customCacheKey= */ "customCacheKey");
|
||||||
|
|
||||||
|
databaseProvider = new ExoDatabaseProvider(context);
|
||||||
|
downloadIndex = new DefaultDownloadIndex(databaseProvider);
|
||||||
|
|
||||||
|
assertEqual(downloadIndex.getDownload("http://www.test.com/manifest.mpd"), dashDownload);
|
||||||
|
assertEqual(downloadIndex.getDownload("http://www.test.com/manifest.m3u8"), hlsDownload);
|
||||||
|
assertEqual(downloadIndex.getDownload("http://www.test.com/video.ism/manifest"), ssDownload);
|
||||||
|
assertEqual(downloadIndex.getDownload("http://www.test.com/video.mp4"), progressiveDownload);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setStopReason_setReasonToNone() throws Exception {
|
public void setStopReason_setReasonToNone() throws Exception {
|
||||||
String id = "id";
|
String id = "id";
|
||||||
|
|
@ -210,10 +268,10 @@ public class DefaultDownloadIndexTest {
|
||||||
Download download = downloadBuilder.build();
|
Download download = downloadBuilder.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
|
|
||||||
downloadIndex.setStopReason(Download.STOP_REASON_NONE);
|
downloadIndex.setStopReason(STOP_REASON_NONE);
|
||||||
|
|
||||||
Download readDownload = downloadIndex.getDownload(id);
|
Download readDownload = downloadIndex.getDownload(id);
|
||||||
Download expectedDownload = downloadBuilder.setStopReason(Download.STOP_REASON_NONE).build();
|
Download expectedDownload = downloadBuilder.setStopReason(STOP_REASON_NONE).build();
|
||||||
assertEqual(readDownload, expectedDownload);
|
assertEqual(readDownload, expectedDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,7 +281,7 @@ public class DefaultDownloadIndexTest {
|
||||||
DownloadBuilder downloadBuilder =
|
DownloadBuilder downloadBuilder =
|
||||||
new DownloadBuilder(id)
|
new DownloadBuilder(id)
|
||||||
.setState(Download.STATE_FAILED)
|
.setState(Download.STATE_FAILED)
|
||||||
.setFailureReason(Download.FAILURE_REASON_UNKNOWN);
|
.setFailureReason(FAILURE_REASON_UNKNOWN);
|
||||||
Download download = downloadBuilder.build();
|
Download download = downloadBuilder.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
int stopReason = 0x12345678;
|
int stopReason = 0x12345678;
|
||||||
|
|
@ -238,7 +296,7 @@ public class DefaultDownloadIndexTest {
|
||||||
@Test
|
@Test
|
||||||
public void setStopReason_notTerminalState_doesNotSetStopReason() throws Exception {
|
public void setStopReason_notTerminalState_doesNotSetStopReason() throws Exception {
|
||||||
String id = "id";
|
String id = "id";
|
||||||
DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(Download.STATE_DOWNLOADING);
|
DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(STATE_DOWNLOADING);
|
||||||
Download download = downloadBuilder.build();
|
Download download = downloadBuilder.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
int notMetRequirements = 0x12345678;
|
int notMetRequirements = 0x12345678;
|
||||||
|
|
@ -255,7 +313,7 @@ public class DefaultDownloadIndexTest {
|
||||||
DownloadBuilder downloadBuilder =
|
DownloadBuilder downloadBuilder =
|
||||||
new DownloadBuilder(id)
|
new DownloadBuilder(id)
|
||||||
.setState(Download.STATE_FAILED)
|
.setState(Download.STATE_FAILED)
|
||||||
.setFailureReason(Download.FAILURE_REASON_UNKNOWN);
|
.setFailureReason(FAILURE_REASON_UNKNOWN);
|
||||||
Download download = downloadBuilder.build();
|
Download download = downloadBuilder.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
|
|
||||||
|
|
@ -263,7 +321,7 @@ public class DefaultDownloadIndexTest {
|
||||||
|
|
||||||
download = downloadIndex.getDownload(id);
|
download = downloadIndex.getDownload(id);
|
||||||
assertThat(download.state).isEqualTo(Download.STATE_REMOVING);
|
assertThat(download.state).isEqualTo(Download.STATE_REMOVING);
|
||||||
assertThat(download.failureReason).isEqualTo(Download.FAILURE_REASON_NONE);
|
assertThat(download.failureReason).isEqualTo(FAILURE_REASON_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -274,10 +332,10 @@ public class DefaultDownloadIndexTest {
|
||||||
Download download = downloadBuilder.build();
|
Download download = downloadBuilder.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
|
|
||||||
downloadIndex.setStopReason(id, Download.STOP_REASON_NONE);
|
downloadIndex.setStopReason(id, STOP_REASON_NONE);
|
||||||
|
|
||||||
Download readDownload = downloadIndex.getDownload(id);
|
Download readDownload = downloadIndex.getDownload(id);
|
||||||
Download expectedDownload = downloadBuilder.setStopReason(Download.STOP_REASON_NONE).build();
|
Download expectedDownload = downloadBuilder.setStopReason(STOP_REASON_NONE).build();
|
||||||
assertEqual(readDownload, expectedDownload);
|
assertEqual(readDownload, expectedDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,7 +345,7 @@ public class DefaultDownloadIndexTest {
|
||||||
DownloadBuilder downloadBuilder =
|
DownloadBuilder downloadBuilder =
|
||||||
new DownloadBuilder(id)
|
new DownloadBuilder(id)
|
||||||
.setState(Download.STATE_FAILED)
|
.setState(Download.STATE_FAILED)
|
||||||
.setFailureReason(Download.FAILURE_REASON_UNKNOWN);
|
.setFailureReason(FAILURE_REASON_UNKNOWN);
|
||||||
Download download = downloadBuilder.build();
|
Download download = downloadBuilder.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
int stopReason = 0x12345678;
|
int stopReason = 0x12345678;
|
||||||
|
|
@ -302,7 +360,7 @@ public class DefaultDownloadIndexTest {
|
||||||
@Test
|
@Test
|
||||||
public void setSingleDownloadStopReason_notTerminalState_doesNotSetStopReason() throws Exception {
|
public void setSingleDownloadStopReason_notTerminalState_doesNotSetStopReason() throws Exception {
|
||||||
String id = "id";
|
String id = "id";
|
||||||
DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(Download.STATE_DOWNLOADING);
|
DownloadBuilder downloadBuilder = new DownloadBuilder(id).setState(STATE_DOWNLOADING);
|
||||||
Download download = downloadBuilder.build();
|
Download download = downloadBuilder.build();
|
||||||
downloadIndex.putDownload(download);
|
downloadIndex.putDownload(download);
|
||||||
int notMetRequirements = 0x12345678;
|
int notMetRequirements = 0x12345678;
|
||||||
|
|
@ -324,4 +382,23 @@ public class DefaultDownloadIndexTest {
|
||||||
assertThat(download.getPercentDownloaded()).isEqualTo(that.getPercentDownloaded());
|
assertThat(download.getPercentDownloaded()).isEqualTo(that.getPercentDownloaded());
|
||||||
assertThat(download.getBytesDownloaded()).isEqualTo(that.getBytesDownloaded());
|
assertThat(download.getBytesDownloaded()).isEqualTo(that.getBytesDownloaded());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Download createDownload(
|
||||||
|
String uri, String mimeType, List<StreamKey> streamKeys, @Nullable String customCacheKey) {
|
||||||
|
return new Download(
|
||||||
|
new DownloadRequest(
|
||||||
|
uri,
|
||||||
|
Uri.parse(uri),
|
||||||
|
mimeType,
|
||||||
|
streamKeys,
|
||||||
|
/* keySetId= */ null,
|
||||||
|
customCacheKey,
|
||||||
|
/* data= */ new byte[] {0, 1, 2, 3}),
|
||||||
|
/* state= */ STATE_STOPPED,
|
||||||
|
/* startTimeMs= */ 1,
|
||||||
|
/* updateTimeMs= */ 2,
|
||||||
|
/* contentLength= */ 3,
|
||||||
|
/* stopReason= */ 4,
|
||||||
|
/* failureReason= */ FAILURE_REASON_NONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,11 @@ public final class DefaultDownloaderFactoryTest {
|
||||||
Downloader downloader =
|
Downloader downloader =
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
/* id= */ "id",
|
||||||
DownloadRequest.TYPE_PROGRESSIVE,
|
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
/* mimeType= */ null,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null));
|
/* data= */ null));
|
||||||
assertThat(downloader).isInstanceOf(ProgressiveDownloader.class);
|
assertThat(downloader).isInstanceOf(ProgressiveDownloader.class);
|
||||||
|
|
|
||||||
|
|
@ -403,8 +403,8 @@ public class DownloadHelperTest {
|
||||||
|
|
||||||
DownloadRequest downloadRequest = downloadHelper.getDownloadRequest(data);
|
DownloadRequest downloadRequest = downloadHelper.getDownloadRequest(data);
|
||||||
|
|
||||||
assertThat(downloadRequest.type).isEqualTo(DownloadRequest.TYPE_PROGRESSIVE);
|
|
||||||
assertThat(downloadRequest.uri).isEqualTo(testMediaItem.playbackProperties.uri);
|
assertThat(downloadRequest.uri).isEqualTo(testMediaItem.playbackProperties.uri);
|
||||||
|
assertThat(downloadRequest.mimeType).isEqualTo(testMediaItem.playbackProperties.mimeType);
|
||||||
assertThat(downloadRequest.customCacheKey)
|
assertThat(downloadRequest.customCacheKey)
|
||||||
.isEqualTo(testMediaItem.playbackProperties.customCacheKey);
|
.isEqualTo(testMediaItem.playbackProperties.customCacheKey);
|
||||||
assertThat(downloadRequest.data).isEqualTo(data);
|
assertThat(downloadRequest.data).isEqualTo(data);
|
||||||
|
|
|
||||||
|
|
@ -790,9 +790,10 @@ public class DownloadManagerTest {
|
||||||
private static DownloadRequest createDownloadRequest(String id, StreamKey... keys) {
|
private static DownloadRequest createDownloadRequest(String id, StreamKey... keys) {
|
||||||
return new DownloadRequest(
|
return new DownloadRequest(
|
||||||
id,
|
id,
|
||||||
DownloadRequest.TYPE_DASH,
|
|
||||||
Uri.parse("http://abc.com/ " + id),
|
Uri.parse("http://abc.com/ " + id),
|
||||||
|
/* mimeType= */ null,
|
||||||
Arrays.asList(keys),
|
Arrays.asList(keys),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null);
|
/* data= */ null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_DASH;
|
|
||||||
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_HLS;
|
|
||||||
import static com.google.android.exoplayer2.offline.DownloadRequest.TYPE_PROGRESSIVE;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
|
@ -48,44 +45,20 @@ public class DownloadRequestTest {
|
||||||
public void mergeRequests_withDifferentIds_fails() {
|
public void mergeRequests_withDifferentIds_fails() {
|
||||||
DownloadRequest request1 =
|
DownloadRequest request1 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id1",
|
/* id= */ "id1",
|
||||||
TYPE_DASH,
|
|
||||||
uri1,
|
uri1,
|
||||||
|
/* mimeType= */ null,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null);
|
/* data= */ null);
|
||||||
DownloadRequest request2 =
|
DownloadRequest request2 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id2",
|
/* id= */ "id2",
|
||||||
TYPE_DASH,
|
|
||||||
uri2,
|
uri2,
|
||||||
|
/* mimeType= */ null,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
/* customCacheKey= */ null,
|
/* keySetId= */ null,
|
||||||
/* data= */ null);
|
|
||||||
try {
|
|
||||||
request1.copyWithMergedRequest(request2);
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// Expected.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mergeRequests_withDifferentTypes_fails() {
|
|
||||||
DownloadRequest request1 =
|
|
||||||
new DownloadRequest(
|
|
||||||
"id1",
|
|
||||||
TYPE_DASH,
|
|
||||||
uri1,
|
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
|
||||||
/* customCacheKey= */ null,
|
|
||||||
/* data= */ null);
|
|
||||||
DownloadRequest request2 =
|
|
||||||
new DownloadRequest(
|
|
||||||
"id1",
|
|
||||||
TYPE_HLS,
|
|
||||||
uri1,
|
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null);
|
/* data= */ null);
|
||||||
try {
|
try {
|
||||||
|
|
@ -135,33 +108,40 @@ public class DownloadRequestTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeRequests_withDifferentFields() {
|
public void mergeRequests_withDifferentFields() {
|
||||||
byte[] data1 = new byte[] {0, 1, 2};
|
byte[] keySetId1 = new byte[] {0, 1, 2};
|
||||||
byte[] data2 = new byte[] {3, 4, 5};
|
byte[] keySetId2 = new byte[] {3, 4, 5};
|
||||||
|
byte[] data1 = new byte[] {6, 7, 8};
|
||||||
|
byte[] data2 = new byte[] {9, 10, 11};
|
||||||
|
|
||||||
DownloadRequest request1 =
|
DownloadRequest request1 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id1",
|
/* id= */ "id1",
|
||||||
TYPE_PROGRESSIVE,
|
|
||||||
uri1,
|
uri1,
|
||||||
|
/* mimeType= */ null,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
"key1",
|
keySetId1,
|
||||||
/* data= */ data1);
|
/* customCacheKey= */ "key1",
|
||||||
|
data1);
|
||||||
DownloadRequest request2 =
|
DownloadRequest request2 =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id1",
|
/* id= */ "id1",
|
||||||
TYPE_PROGRESSIVE,
|
|
||||||
uri2,
|
uri2,
|
||||||
|
/* mimeType= */ null,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
"key2",
|
keySetId2,
|
||||||
/* data= */ data2);
|
/* customCacheKey= */ "key2",
|
||||||
|
data2);
|
||||||
|
|
||||||
// uri, customCacheKey and data should be from the request being merged.
|
// uri, keySetId, customCacheKey and data should be from the request being merged.
|
||||||
DownloadRequest mergedRequest = request1.copyWithMergedRequest(request2);
|
DownloadRequest mergedRequest = request1.copyWithMergedRequest(request2);
|
||||||
assertThat(mergedRequest.uri).isEqualTo(uri2);
|
assertThat(mergedRequest.uri).isEqualTo(uri2);
|
||||||
|
assertThat(mergedRequest.keySetId).isEqualTo(keySetId2);
|
||||||
assertThat(mergedRequest.customCacheKey).isEqualTo("key2");
|
assertThat(mergedRequest.customCacheKey).isEqualTo("key2");
|
||||||
assertThat(mergedRequest.data).isEqualTo(data2);
|
assertThat(mergedRequest.data).isEqualTo(data2);
|
||||||
|
|
||||||
mergedRequest = request2.copyWithMergedRequest(request1);
|
mergedRequest = request2.copyWithMergedRequest(request1);
|
||||||
assertThat(mergedRequest.uri).isEqualTo(uri1);
|
assertThat(mergedRequest.uri).isEqualTo(uri1);
|
||||||
|
assertThat(mergedRequest.keySetId).isEqualTo(keySetId1);
|
||||||
assertThat(mergedRequest.customCacheKey).isEqualTo("key1");
|
assertThat(mergedRequest.customCacheKey).isEqualTo("key1");
|
||||||
assertThat(mergedRequest.data).isEqualTo(data1);
|
assertThat(mergedRequest.data).isEqualTo(data1);
|
||||||
}
|
}
|
||||||
|
|
@ -173,12 +153,13 @@ public class DownloadRequestTest {
|
||||||
streamKeys.add(new StreamKey(4, 5, 6));
|
streamKeys.add(new StreamKey(4, 5, 6));
|
||||||
DownloadRequest requestToParcel =
|
DownloadRequest requestToParcel =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
/* id= */ "id",
|
||||||
"type",
|
|
||||||
Uri.parse("https://abc.def/ghi"),
|
Uri.parse("https://abc.def/ghi"),
|
||||||
|
/* mimeType= */ null,
|
||||||
streamKeys,
|
streamKeys,
|
||||||
"key",
|
/* keySetId= */ new byte[] {1, 2, 3, 4, 5},
|
||||||
new byte[] {1, 2, 3, 4, 5});
|
/* customCacheKey= */ "key",
|
||||||
|
/* data= */ new byte[] {1, 2, 3, 4, 5});
|
||||||
Parcel parcel = Parcel.obtain();
|
Parcel parcel = Parcel.obtain();
|
||||||
requestToParcel.writeToParcel(parcel, 0);
|
requestToParcel.writeToParcel(parcel, 0);
|
||||||
parcel.setDataPosition(0);
|
parcel.setDataPosition(0);
|
||||||
|
|
@ -232,11 +213,18 @@ public class DownloadRequestTest {
|
||||||
private static void assertEqual(DownloadRequest request1, DownloadRequest request2) {
|
private static void assertEqual(DownloadRequest request1, DownloadRequest request2) {
|
||||||
assertThat(request1).isEqualTo(request2);
|
assertThat(request1).isEqualTo(request2);
|
||||||
assertThat(request2).isEqualTo(request1);
|
assertThat(request2).isEqualTo(request1);
|
||||||
|
assertThat(request1.hashCode()).isEqualTo(request2.hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DownloadRequest createRequest(Uri uri, StreamKey... keys) {
|
private static DownloadRequest createRequest(Uri uri, StreamKey... keys) {
|
||||||
return new DownloadRequest(
|
return new DownloadRequest(
|
||||||
uri.toString(), TYPE_DASH, uri, toList(keys), /* customCacheKey= */ null, /* data= */ null);
|
uri.toString(),
|
||||||
|
uri,
|
||||||
|
/* mimeType= */ null,
|
||||||
|
toList(keys),
|
||||||
|
/* keySetId= */ null,
|
||||||
|
/* customCacheKey= */ null,
|
||||||
|
/* data= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<StreamKey> toList(StreamKey... keys) {
|
private static List<StreamKey> toList(StreamKey... keys) {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -93,9 +94,10 @@ public class DashDownloaderTest {
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
"id",
|
||||||
DownloadRequest.TYPE_DASH,
|
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
MimeTypes.APPLICATION_MPD,
|
||||||
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
|
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null));
|
/* data= */ null));
|
||||||
assertThat(downloader).isInstanceOf(DashDownloader.class);
|
assertThat(downloader).isInstanceOf(DashDownloader.class);
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import com.google.android.exoplayer2.upstream.DataSource.Factory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -224,9 +225,10 @@ public class DownloadManagerDashTest {
|
||||||
Collections.addAll(keysList, keys);
|
Collections.addAll(keysList, keys);
|
||||||
return new DownloadRequest(
|
return new DownloadRequest(
|
||||||
TEST_ID,
|
TEST_ID,
|
||||||
DownloadRequest.TYPE_DASH,
|
|
||||||
TEST_MPD_URI,
|
TEST_MPD_URI,
|
||||||
|
MimeTypes.APPLICATION_MPD,
|
||||||
keysList,
|
keysList,
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
import com.google.android.exoplayer2.util.ConditionVariable;
|
import com.google.android.exoplayer2.util.ConditionVariable;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -209,9 +210,10 @@ public class DownloadServiceDashTest {
|
||||||
DownloadRequest action =
|
DownloadRequest action =
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
TEST_ID,
|
TEST_ID,
|
||||||
DownloadRequest.TYPE_DASH,
|
|
||||||
TEST_MPD_URI,
|
TEST_MPD_URI,
|
||||||
|
MimeTypes.APPLICATION_MPD,
|
||||||
keysList,
|
keysList,
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
null);
|
null);
|
||||||
testThread.runOnMainThread(
|
testThread.runOnMainThread(
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor;
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -111,9 +112,10 @@ public class HlsDownloaderTest {
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
"id",
|
||||||
DownloadRequest.TYPE_HLS,
|
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
MimeTypes.APPLICATION_M3U8,
|
||||||
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
|
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null));
|
/* data= */ null));
|
||||||
assertThat(downloader).isInstanceOf(HlsDownloader.class);
|
assertThat(downloader).isInstanceOf(HlsDownloader.class);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import com.google.android.exoplayer2.offline.StreamKey;
|
||||||
import com.google.android.exoplayer2.upstream.DummyDataSource;
|
import com.google.android.exoplayer2.upstream.DummyDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.Cache;
|
import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
@ -49,9 +50,10 @@ public final class SsDownloaderTest {
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
new DownloadRequest(
|
new DownloadRequest(
|
||||||
"id",
|
"id",
|
||||||
DownloadRequest.TYPE_SS,
|
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
MimeTypes.APPLICATION_SS,
|
||||||
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
|
Collections.singletonList(new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0)),
|
||||||
|
/* keySetId= */ null,
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null));
|
/* data= */ null));
|
||||||
assertThat(downloader).isInstanceOf(SsDownloader.class);
|
assertThat(downloader).isInstanceOf(SsDownloader.class);
|
||||||
|
|
|
||||||
BIN
testdata/src/test/assets/offline/exoplayer_internal_v2.db
vendored
Normal file
BIN
testdata/src/test/assets/offline/exoplayer_internal_v2.db
vendored
Normal file
Binary file not shown.
|
|
@ -38,9 +38,10 @@ public final class DownloadBuilder {
|
||||||
private final DownloadProgress progress;
|
private final DownloadProgress progress;
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private String type;
|
|
||||||
private Uri uri;
|
private Uri uri;
|
||||||
|
@Nullable private String mimeType;
|
||||||
private List<StreamKey> streamKeys;
|
private List<StreamKey> streamKeys;
|
||||||
|
private byte[] keySetId;
|
||||||
@Nullable private String cacheKey;
|
@Nullable private String cacheKey;
|
||||||
private byte[] customMetadata;
|
private byte[] customMetadata;
|
||||||
|
|
||||||
|
|
@ -52,18 +53,19 @@ public final class DownloadBuilder {
|
||||||
private int failureReason;
|
private int failureReason;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a download builder for "uri" with type "type" and no stream keys.
|
* Creates a download builder for "uri" and no stream keys.
|
||||||
*
|
*
|
||||||
* @param id The unique content identifier for the download.
|
* @param id The unique content identifier for the download.
|
||||||
*/
|
*/
|
||||||
public DownloadBuilder(String id) {
|
public DownloadBuilder(String id) {
|
||||||
this(
|
this(
|
||||||
id,
|
id,
|
||||||
"type",
|
|
||||||
Uri.parse("uri"),
|
Uri.parse("uri"),
|
||||||
|
/* mimeType= */ null,
|
||||||
/* streamKeys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
|
/* keySetId= */ new byte[0],
|
||||||
/* cacheKey= */ null,
|
/* cacheKey= */ null,
|
||||||
new byte[0]);
|
/* customMetadata= */ new byte[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,9 +76,10 @@ public final class DownloadBuilder {
|
||||||
public DownloadBuilder(DownloadRequest request) {
|
public DownloadBuilder(DownloadRequest request) {
|
||||||
this(
|
this(
|
||||||
request.id,
|
request.id,
|
||||||
request.type,
|
|
||||||
request.uri,
|
request.uri,
|
||||||
|
request.mimeType,
|
||||||
request.streamKeys,
|
request.streamKeys,
|
||||||
|
request.keySetId,
|
||||||
request.customCacheKey,
|
request.customCacheKey,
|
||||||
request.data);
|
request.data);
|
||||||
}
|
}
|
||||||
|
|
@ -84,15 +87,17 @@ public final class DownloadBuilder {
|
||||||
/** Creates a download builder. */
|
/** Creates a download builder. */
|
||||||
private DownloadBuilder(
|
private DownloadBuilder(
|
||||||
String id,
|
String id,
|
||||||
String type,
|
|
||||||
Uri uri,
|
Uri uri,
|
||||||
|
@Nullable String mimeType,
|
||||||
List<StreamKey> streamKeys,
|
List<StreamKey> streamKeys,
|
||||||
|
byte[] keySetId,
|
||||||
@Nullable String cacheKey,
|
@Nullable String cacheKey,
|
||||||
byte[] customMetadata) {
|
byte[] customMetadata) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
|
this.mimeType = mimeType;
|
||||||
this.streamKeys = streamKeys;
|
this.streamKeys = streamKeys;
|
||||||
|
this.keySetId = keySetId;
|
||||||
this.cacheKey = cacheKey;
|
this.cacheKey = cacheKey;
|
||||||
this.customMetadata = customMetadata;
|
this.customMetadata = customMetadata;
|
||||||
this.state = Download.STATE_QUEUED;
|
this.state = Download.STATE_QUEUED;
|
||||||
|
|
@ -101,12 +106,6 @@ public final class DownloadBuilder {
|
||||||
this.progress = new DownloadProgress();
|
this.progress = new DownloadProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see DownloadRequest#type */
|
|
||||||
public DownloadBuilder setType(String type) {
|
|
||||||
this.type = type;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see DownloadRequest#uri */
|
/** @see DownloadRequest#uri */
|
||||||
public DownloadBuilder setUri(String uri) {
|
public DownloadBuilder setUri(String uri) {
|
||||||
this.uri = Uri.parse(uri);
|
this.uri = Uri.parse(uri);
|
||||||
|
|
@ -119,6 +118,18 @@ public final class DownloadBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @see DownloadRequest#mimeType */
|
||||||
|
public DownloadBuilder setMimeType(String mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see DownloadRequest#keySetId */
|
||||||
|
public DownloadBuilder setKeySetId(byte[] keySetId) {
|
||||||
|
this.keySetId = keySetId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** @see DownloadRequest#customCacheKey */
|
/** @see DownloadRequest#customCacheKey */
|
||||||
public DownloadBuilder setCacheKey(@Nullable String cacheKey) {
|
public DownloadBuilder setCacheKey(@Nullable String cacheKey) {
|
||||||
this.cacheKey = cacheKey;
|
this.cacheKey = cacheKey;
|
||||||
|
|
@ -187,7 +198,7 @@ public final class DownloadBuilder {
|
||||||
|
|
||||||
public Download build() {
|
public Download build() {
|
||||||
DownloadRequest request =
|
DownloadRequest request =
|
||||||
new DownloadRequest(id, type, uri, streamKeys, cacheKey, customMetadata);
|
new DownloadRequest(id, uri, mimeType, streamKeys, keySetId, cacheKey, customMetadata);
|
||||||
return new Download(
|
return new Download(
|
||||||
request,
|
request,
|
||||||
state,
|
state,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue